求职 面试遇到的问题, 没做出来, 有同行看看嘛? 最好能个 demo

cheng_sukai · 2022年08月15日 · 最后由 cheng_sukai 回复于 2022年08月16日 · 825 次阅读

Ruby 可以通过Thread#raise向另外一个线程抛异常来中断另一个线程。

t = Time.now.to_i + 5
flag = true
ta = Thread.new(flag) do
  while flag do
    puts 'Thread A...'
    sleep 1
    if Time.now.to_i > t
      puts 'Thread A quit...'
      flag = false
    end
  end
end

tb = Thread.new(flag) do
  while flag do
    puts 'Thread B...'
    sleep 1
  end
end

tc = Thread.new(flag) do
  while flag do
    puts 'Thread C...'
    sleep 1
  end
end
[ta, tb, tc].each(&:join)

考的应该是线程间如何通信,有以下方式

  • IPC(inner process communication) 通信方式都适用于线程间通信,比如:文件、网络
  • 线程之间共享内存:全局变量
# inner-thread communication by memory
$status = :pending

def done?
  $status == :done
end

def done!
  puts "done!"
  $status = :done
end

def main
  thread_a = Thread.new do
    loop do
      sleep(0.5)

      if rand(100) < 10
        done!
        puts 'exit thread_a'
        break
      end
    end
  end

  thread_c = Thread.new do
    loop do
      if done?
        puts 'exit thread_c'
        break
      end
      sleep(0.5)
      done! if rand(100) < 10
    end
  end

  threads = [thread_a, thread_c]
  threads.each(&:join)
end
main

如 1 楼分享的帖子,多线程应用在部署时要 graceful 地停止服务(停止处理新请求),就得用到线程间通讯。sidekiq 和 puma 都是 multi-threads 的并发实现模型,它们会做的比较优雅,不是直接简单地用全局变量,而是用一个 manager 的单例(single instance)来通知其他工作线程。

Sidekiq

  • manager 这个单例专门管理其他工作线程:start, quite, stop
  • processor 工作线程,负责具体任务:从队列取 jobs,执行具体 job

@radiocontroller @hjiangwen 按照多线程编程的道理,多线程读写共享的变量是不是应该将其锁起来呢?

u1435638317 回复

是的,多谢提醒,并发编程得时刻惦记着锁👍。但是对于几乎并发操作的场景,应该可以忽略吧

u1435638317 回复

如果线程内对变量只有一个写操作或者都是读操作,大部分场景应该都不需要上锁。但是如果在线程内对这个变量有多个操作的话,比如写和读,是需要上锁的,否则你刚改完,后面再读就可能被其他线程改了,读到的结果可能就不是你希望的

conditional variable + pthread_kill?

只有 thread a 可以改那个 thread variable,其他 thread 执行完了,等那个 conditional variable

pthread_kill 干掉执行一半的 thread。

假设 A 先执行完,那么其他线程就立即结束

这句话我没理解。。。是说 B,C 先执行完的话,要等 A?没先执行完,就要被马上中断?

yfractal 回复

可能说的比较抽象,我举个例子哈:比如下载一个文件,但是呢我开了三个线程同时下载,其中只要有一个下载完,那么其余的线程就不用再继续下载了,直接结束就可以了

require 'thread'

t1, t2, t3 = nil, nil, nil

def download
  sleep rand(5..10)
end

t1 = Thread.new do
  t = Time.now
  download

  t2.raise "STOP"
  t3.raise "STOP"

  puts "t1 download done."
rescue
  puts "abort t1"
ensure
  # teardown
  puts "t1 duration: #{Time.now - t}"
end


t2 = Thread.new do
  t = Time.now
  download

  t1.raise "STOP"
  t3.raise "STOP"

  puts "t2 download done."
rescue
  puts "abort t2"
ensure
  # teardown
  puts "t2 duration: #{Time.now - t}"
end

t3 = Thread.new do
  t = Time.now
  download

  t1.raise "STOP"
  t2.raise "STOP"

  puts "t3 download done."
rescue
  puts "abort t3"
ensure
  # teardown
  puts "t3 duration: #{Time.now - t}"
end

[t1, t2, t3].map(&:join)
cheng_sukai 回复

这个例子感觉怪怪的,三个线程同时下载,重复做一件事情有什么作用呢?多线程下载都是协作下载的。这个题目有没有实际的场景描述呢?我也有些不理解

Rei 回复

哈哈通透

谢谢各位大佬的解答,面试还是凉了,暂时还没看懂, == 不过思路应该都一样。我是搞Java的😂(因为生活在 ruby 社区的大家真的很好,遇到问题也都不吝赐教,我特别喜欢这里~~,所以遇到不会的就来这里发帖,每次都有收获) ,谢谢你们啦

需要 登录 后方可回复, 如果你还没有账号请 注册新账号