Ruby中为何给exit加Mutex锁仍会多次输出‘Exiting’?
为什么Ruby多线程中加了Mutex调用exit还是会多次打印"Exiting"?
咱们先看你给出的两段代码,首先第一段:
def print_and_exit puts "Exiting" exit end Array.new(100) { Thread.new { sleep 1; print_and_exit } }.each(&:join)
这段代码多次打印“Exiting”很好理解——100个线程同时醒来后,都会抢着执行print_and_exit,在进程真正退出前,大量线程都有机会跑完puts这一行。
那为什么加了Mutex之后结果还是一样呢?
def print_and_exit puts "Exiting" exit end mutex = Mutex.new Array.new(100) { Thread.new { sleep 1; mutex.lock; print_and_exit } }.each(&:join)
问题出在Ruby的exit方法的工作机制上:exit并不会立刻终止进程。它会先触发VM的清理流程——包括运行所有注册的at_exit钩子、尝试正常终止所有线程,而在这个清理过程完成之前,VM还会继续调度线程。更关键的是,Ruby在退出时会自动释放所有持有的Mutex锁,这就导致第一个线程调用exit后,它持有的Mutex会被释放,其他线程就能抢到锁,进入print_and_exit方法执行puts "Exiting",直到进程最终完成退出流程。
解决办法
要实现“只有一个线程执行退出逻辑”的目标,我们需要在调用exit之前,确保其他线程不会再进入退出逻辑。可以用一个状态标志配合Mutex来实现:
def print_and_exit(mutex, exited_flag) mutex.synchronize do # 检查是否已经有线程触发过退出,有的话直接返回 return if exited_flag[:triggered] # 标记已经触发退出 exited_flag[:triggered] = true end puts "Exiting" exit end mutex = Mutex.new # 用哈希存标志(因为Ruby的Fixnum是不可变的,哈希可以在Mutex保护下修改) exited_flag = { triggered: false } Array.new(100) { Thread.new { sleep 1; print_and_exit(mutex, exited_flag) } }.each(&:join)
这个方案里,我们在Mutex的保护下检查退出标志,只有第一个拿到锁的线程会设置标志并执行打印和退出,后续线程看到标志已经被设置,就直接返回,不会再执行puts和exit。这样就能保证“Exiting”只会被打印一次。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

