为什么以下ruby代码不起作用?
2 | require 'thread'
3 |
4 | $mutex = Mutex.new
5 | $mutex.lock
6 |
7 | t = Thread.new {
8 | sleep 10
9 | $mutex.unlock
10 | }
11 |
12 | $mutex.lock
13 | puts "Delayed hello"
当我运行它时,我收到一个错误:
./test.rb:13:in `lock': thread 0x7f4557856378 tried to join itself (ThreadError)
from ./test.rb:13
在不加入两个线程的情况下同步两个线程的正确方法是什么(两个线程必须在同步后继续运行)?
答案 0 :(得分:6)
这已经过时了,但是我很有贡献,因为它有点可怕,其他答案(在撰写本文时)似乎都不正确。原始代码显然是在尝试:
@ user2413915:您的解决方案省略了在主线程中再次锁定的步骤,因此它不会按预期等待生成的线程。
@Paul Rubel:您的代码假定生成的线程在主线程之前获得了对互斥锁的锁定。这是竞争条件。如果主线程继续执行并首先锁定,则生成的线程将被阻塞,直到主线程打印出之后的"延迟问候"这与所需结果完全相反。您可能通过粘贴到IRB提示符来运行它;如果您尝试修改示例,以便end
和Mutex锁在同一行,它就会失败,过早打印消息(即" end; $mutex.lock
&#34 )。无论哪种方式,它都依赖于Ruby运行时的行为。
原始代码实际上应该在原则上正常工作,虽然可以说缺乏优雅 - 实际上Ruby 1.9+运行时不会允许它,因为它"看到"主线程中的两个连续锁没有解锁,并且没有实现"这是一个产生解锁的衍生线程。 Ruby(在这种情况下在技术上是错误的)引发了一个ThreadError死锁异常。
相反,狡猾地使用ruby Queue。当您尝试从队列中取出某些内容时,该呼叫将阻止,直到某个项目可用。所以:
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"
如果生成的线程首先运行并推入队列,那么主线程将只弹出该项并继续运行。如果主线程在生成的线程推送之前尝试弹出,它将等待生成的线程。
[编辑:请注意,推送到队列的对象可能是生成线程的处理任务的结果,因此主线程会等到处理完成并一次性获得处理结果]。
我已经通过rbenv
成功地在Ruby 1.8.7-p375和Ruby 2.1.2上对此进行了测试,因此可以合理地假设标准库Queue类在所有方面都有效常见的主要Ruby版本。
答案 1 :(得分:-1)
您无需再次拨打第12行的互斥锁。
require 'thread'
$mutex = Mutex.new
$mutex.lock
t = Thread.new {
sleep 10
$mutex.unlock
}
puts "Delayed hello"
这样可行。