嗨,我正在使用ruby中的线程和条件变量,并且得到了一些非常混乱的结果,这是没有意义的。我正在按照ruby文档中的ConditionVariable example进行操作,一切似乎都按计划进行:
mutex = Mutex.new
resource = ConditionVariable.new
waiting_thread = Thread.new {
mutex.synchronize {
puts "Thread 'a' now needs the resource"
resource.wait(mutex)
puts "'a' can now have the resource"
"a can now have the resource"
}
}
signal_thread = Thread.new {
mutex.synchronize {
puts "Thread 'b' has finished using the resource"
resource.signal
}
}
运行此代码时,我得到非常期望的输出:
=> Thread 'a' now needs the resource
=> Thread 'b' has finished using the resource
=> 'a' can now have the resource
无论如何,我将其更改为join
或从value
获得waiting_thread
的瞬间,都爆出了Deadlock
致命错误。
waiting_thread.value
signal_thread
输出:
=失败/错误:waiting_thread.value- 没有活动线程了。僵局?
我可以模糊地了解正在发生的事情-当waiting_thread
无限期锁定时,两者都试图在同一个互斥锁上进行同步。
但是在那种情况下,为什么初始代码可以完美地工作以预期的异步结果给出put
语句?
这不仅对我的理解很重要,而且对于同时进行测试也很重要。如何将join
和value
与ConditionVariables
结合使用以产生所需的内容?
答案 0 :(得分:1)
我认为Ruby文档中的代码可能会产生误导,因为它不会告诉您,如果接收方不等待,发送信号不会在任何地方缓冲。
因此,将导致死锁的情况将发生如下情况:
signal_thread
进入关键部分并调用resource.signal
。该信号将丢失。
signal_thread
完成,然后退出。
waiting_thread
进入关键部分并调用resource.wait
。现在它被锁定,等待一个永远不会到来的信号。
所有线程已锁定或处于非活动状态。没有更多活动线程,因此没有人能够唤醒waiting_thread
-->
死锁错误。
如果继续运行,则示例代码可能会随机出现死锁错误,具体取决于您的CPU,操作系统以及太阳或月亮的位置,因为执行signal_thread
的顺序waiting_thread
是不确定的。该顺序是随机的,因此可能会或可能不会发生死锁,但是死锁可能会根据执行顺序而发生。
现在您如何解决呢?好吧,您需要保证waiting_thread
在signal_thread
发出信号之前等待。我们可以使用Queue
来做到这一点,就像这样:
mutex = Mutex.new
resource = ConditionVariable.new
sync_queue = Queue.new
waiting_thread = Thread.new {
mutex.synchronize {
puts "Waiting thread sending sync message..."
sync_queue << 1
puts "Thread 'a' now needs the resource"
resource.wait(mutex)
puts "'a' can now have the resource"
"a can now have the resource"
}
}
signal_thread = Thread.new {
puts "Signal thread waiting for sync..."
# signal_thread will sleep here, until there is something in the queue to pop.
# This guarantees the right execution order.
sync_queue.pop
mutex.synchronize {
puts "Thread 'b' has finished using the resource"
resource.signal
}
}
waiting_thread.value
现在,代码是确定性的,waiting_thread
将始终在signal_thread
信号之前等待,并且代码将按预期工作。
您只需要知道,如果没有人在另一端等待,则对条件变量的signal
调用会冒烟。我认为文档中缺少此重要信息。
此外,由于这个问题,资源示例实际上不是检查关键部分中资源是否可用的很好示例。如果signal_thread
已经使用了资源,那么waiting_thread
将永远不会知道它。
在实际情况下,线程之间需要共享其他数据,以便一个线程可以检查资源是否正在使用,只有THEN等待信号。如果尚未使用该资源,则不需要等待信号,并且实际上根本不应该这样做。
即ConditionVariable不应用于检查资源状态,而仅用于信令。在这种情况下,我们将更适当地使用条件变量。