交换例程共享互斥锁
我有a
在一些随机时间重复调用的方法,它触发方法b
,它在一些随机时间之后完全执行并且在它自己的线程中。 我想确保等待的后续执行直到b
完成,这是由当前执行的a
触发a
。 换句话说, a
和b
将被替代地执行。 我尝试使用互斥和条件变量执行此操作,如下所示:
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
应该完成。
我期望交替执行a
和b
,从而交替地打印"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)}