我有一个方法a
,它在某个随机时间重复调用,触发方法b
,它在一些随机时间之后完全执行并且在它自己的线程中。我想确保后续执行a
等待b
完成,这由当前执行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
中的a
确保在{{1}之前不会调用方法$cv.signal
中的$mutex.synchronize do ... end
(也在b
内)将$cv.wait($mutex)
设置为信号的聆听模式。这一点在the document中给出。
我打算在方法$cv
中分配给$mutex.synchronize do ... end
的另一个函数是避免连续执行方法a
。我的理由是方法a
中的$cv.wait($mutex)
应避免a
完成并发布,直到调用方法$mutex
中的$cv.signal
为止b
应该完成。
我希望替代执行b
和a
,从而替代地打印b
和"a"
。但实际上,他们不是; "b"
或"a"
中的每一个都可以连续打印。
在那之后,我认为上面的推理可能是错误的"b"
即使$mutex
(或$cv
)处于等待模式,$mutex
已完全释放$cv.wait($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
但这没有效果。
如何解决这个问题?或者,我对此有何不妥?
答案 0 :(得分:2)
我没有为您提供解决方案,但是a
被调用的原因是wait
是否超出预期signal
释放对互斥锁的锁定?否则永远不会调用a
。这似乎发生了如预期的那样#34;第一次,但在那之后,你最终排队了几个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)}
线程唤醒并锁定之前它们潜入mutex再次。
如果穷人在任何时候都会检测你的代码,你可以看到它发生了:
{{1}}
答案 1 :(得分:1)
我知道这听起来很奇怪,但使用队列阻止这些线程会更容易:
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)}