这是旧的,但我正在贡献,因为其他答案(在撰写本文时)似乎都不正确有点可怕。原始代码显然试图:
- 在主线程中创建一个互斥锁并锁定它。
- 启动一个新线程,该线程可以在任何时间开始运行,并且在任何延迟之后都会受到 Ruby 运行时的突发奇想。
- 仅在完成工作后才让该线程解锁互斥锁。
- 然后让主线程故意重新锁定互斥锁,目的是它产生一个将解锁它的线程。主线程等待。
- 然后主线程继续运行。
@user2413915:您的解决方案省略了在主线程中再次锁定的步骤,因此它不会按预期等待生成的线程。
@Paul Rubel:您的代码假定生成的线程在主线程之前获得了互斥锁的锁定。这是一个竞争条件。如果主线程继续执行并首先锁定,则生成的线程将被阻塞,直到主线程打印“Delayed hello”,这与期望的结果完全相反。您可能通过粘贴到 IRB 提示符来运行它;如果您尝试修改示例以使end
和互斥锁在同一行,它将失败,过早打印消息(即“ end; $mutex.lock
”)。无论哪种方式,它都依赖于偶然工作的 Ruby 运行时的行为。
原始代码在原则上实际上应该可以正常工作,尽管可以说缺乏优雅 - 实际上 Ruby 1.9+ 运行时不会允许它,因为它在没有解锁的情况下“看到”主线程中的两个连续锁并且没有“实现”有一个衍生线程将进行解锁。Ruby(在这种情况下在技术上是错误的)引发 ThreadError 死锁异常。
相反,巧妙地使用 ruby 队列。当您尝试从队列中提取某些内容时,调用将阻塞,直到有可用的项目。所以:
require 'thread'
require 'queue'
queue = Queue.new
t = Thread.new {
sleep 10
queue.push( nil ) # Push any object you like - here, it's a NilClass instance
}
queue.pop() # Blocks until thread 't' pushes onto the queue
puts "Delayed hello"
如果生成的线程首先运行并推送到队列中,那么主线程只会弹出该项目并继续运行。如果主线程在派生线程推送之前尝试弹出,它将等待派生线程。
[编辑:请注意,推入队列的对象可能是派生线程处理任务的结果,因此主线程等待处理完成并一次性获得处理结果]。
我已经在 Ruby 1.8.7-p375 和 Ruby 2.1.2 viarbenv
上成功地测试了这一点,因此可以合理地假设标准库 Queue 类在所有常见的主要 Ruby 版本中都可以正常工作。