交换例程共享互斥锁

我有a在一些随机时间重复调用的方法,它触发方法b ,它在一些随机时间之后完全执行并且在它自己的线程中。 我想确保等待的后续执行直到b完成,这是由当前执行的a触发a 。 换句话说, ab将被替代地执行。 我尝试使用互斥和条件变量执行此操作,如下所示:

 def a Thread.new do $mutex.synchronize do puts "a" b $cv.wait($mutex) end end end def b Thread.new do sleep(rand) $mutex.synchronize do puts "b" $cv.signal end end end $mutex, $cv = Mutex.new, ConditionVariable.new loop{a; sleep(rand)} 

在这段代码中, $mutex.synchronize do ... end in方法a确保在$cv.wait($mutex)之前不会调用方法b中的$cv.signal (也在另一个$mutex.synchronize do ... end$cv.wait($mutex)$cv设置为信号的聆听模式。 这在文件中给出了很多。

我打算分配给$mutex.synchronize do ... end另一个函数$mutex.synchronize do ... end in method a是为了避免连续执行方法a 。 我的理由是方法a中的$cv.wait($mutex)应该避免$mutex被完成并释放,直到调用方法b $cv.signal ,到那时b应该完成。

我期望交替执行ab ,从而交替地打印"a""b" 。 但实际上,他们不是; "a""b""a"都可以连续打印。


在那之后,我认为上面的推理可能是错误的,因为$cv.wait($mutex)调用后,即使$cv (或$mutex )处于等待模式, $mutex也会被完全释放。 所以我在a添加了一些虚拟过程,将其更改为:

 def a Thread.new do $mutex.synchronize do puts "a" b $cv.wait($mutex) nil # Dummy process intended to keep `$mutex` locked until `$cv` is released end end end 

但那没有效果。


怎么解决这个问题? 或者,我对此有何不妥?

我知道这听起来很奇怪,但使用队列来阻止这些线程会更容易:

 def a Thread.new do $queue.pop puts "a" b end end def b Thread.new do sleep(rand) puts "b" $queue << true end end $queue = Queue.new $queue << true loop{a; sleep(rand)} 

我没有给你一个解决方案,但是这不是a被调用的原因超过你期望的是wait释放互斥锁上的锁? 否则无法调用signal 。 这似乎是第一次“按预期”发生,但在那之后,你最终有几个线程排队,痒进入synchronize块,并且他们潜入b线程唤醒并再次锁定互斥锁。

如果穷人在每个转弯处都会检测你的代码,你可以看到它发生了:

 def a puts("a before thread #{Thread.current}") Thread.new do puts(" a synch0 #{Thread.current}") $mutex.synchronize do puts(" a before b #{Thread.current}") b puts(" a after b, before wait #{Thread.current}") $cv.wait($mutex) puts(" a after wait #{Thread.current}") end puts(" a synch1 #{Thread.current}") end puts("a after thread #{Thread.current}") end def b puts("b before thread #{Thread.current}") Thread.new do puts(" b before sleep #{Thread.current}") sleep(rand) puts(" b after sleep, synch0 #{Thread.current}") $mutex.synchronize do puts(" b before signal #{Thread.current}") $cv.signal puts(" b after signal #{Thread.current}") end puts(" b synch1 #{Thread.current}") end puts("b after thread #{Thread.current}") end $mutex, $cv = Mutex.new, ConditionVariable.new loop{a; sleep(rand)}