Ruby 如何使用条件变量?
Ruby中关于条件变量的资源不多,但大多数都是错误的。就像,或者——他们都可能陷入僵局 我们可以通过按给定顺序启动线程来解决这个问题,并且可能在线程之间放置一些Ruby 如何使用条件变量?,ruby,mutex,deadlock,race-condition,producer-consumer,Ruby,Mutex,Deadlock,Race Condition,Producer Consumer,Ruby中关于条件变量的资源不多,但大多数都是错误的。就像,或者——他们都可能陷入僵局 我们可以通过按给定顺序启动线程来解决这个问题,并且可能在线程之间放置一些睡眠,以强制执行同步。但这只是推迟了真正的问题 我将代码改写成经典的: 需要“线程” 队列=[] mutex=mutex.new resource=ConditionVariable.new 线程=[] 线程基于一个问题,我提出了一个可行的解决方案。它强制执行线程之间的交替,这并不理想。我们想要消费者和生产者的多线程是什么 queue =
睡眠,以强制执行同步。但这只是推迟了真正的问题
我将代码改写成经典的:
需要“线程”
队列=[]
mutex=mutex.new
resource=ConditionVariable.new
线程=[]
线程基于一个问题,我提出了一个可行的解决方案。它强制执行线程之间的交替,这并不理想。我们想要消费者和生产者的多线程是什么
queue = []
mutex = Mutex.new
threads = []
next_run = :producer
cond_consumer = ConditionVariable.new
cond_producer = ConditionVariable.new
threads << Thread.new do
5.times do |i|
mutex.synchronize do
until next_run == :consumer
cond_consumer.wait(mutex)
end
value = queue.pop
print "consumed #{value}\n"
next_run = :producer
cond_producer.signal
end
end
end
threads << Thread.new do
5.times do |i|
mutex.synchronize do
until next_run == :producer
cond_producer.wait(mutex)
end
queue << i
print "#{i} produced\n"
next_run = :consumer
cond_consumer.signal
end
end
end
threads.each(&:join)
queue=[]
mutex=mutex.new
线程=[]
下一次运行=:producer
cond_consumer=ConditionVariable.new
cond_producer=ConditionVariable.new
线程您可以简化您的问题:
require 'thread'
queue = Queue.new
consumer = Thread.new { queue.pop }
consumer.join
由于主线程正在等待使用者线程退出,但使用者线程正在休眠(由于queue.pop
),因此会导致:
producer-consumer.rb:4:in `join': deadlock detected (fatal)
from producer-consumer.rb:4:in `<main>'
这是一个更健壮的解决方案,有多个消费者和生产者,并且使用了,MonitorMixin
有一个特殊的ConditionVariable
和wait\u-until()
方法
require 'monitor'
queue = []
queue.extend(MonitorMixin)
cond = queue.new_cond
consumers, producers = [], []
for i in 0..5
consumers << Thread.start(i) do |i|
print "consumer start #{i}\n"
while (producers.any?(&:alive?) || !queue.empty?)
queue.synchronize do
cond.wait_while { queue.empty? }
print "consumer #{i}: #{queue.shift}\n"
end
sleep(0.2) #simulate expense
end
end
end
for i in 0..3
producers << Thread.start(i) do |i|
id = (65+i).chr
for j in 0..10 do
queue.synchronize do
item = "#{j} #{id}"
queue << item
print "producer #{id}: produced #{item}\n"
j += 1
cond.broadcast
end
sleep(0.1) #simulate expense
end
end
end
sleep 0.1 while producers.any?(&:alive?)
sleep 0.1 while consumers.any?(&:alive?)
print "queue size #{queue.size}\n"
需要“监视器”
队列=[]
queue.extend(MonitorMixin)
cond=queue.new\u cond
消费者、生产者=[]、[]
因为我在0..5
consumers问题在于,正如您之前所评论的,只有在您能够保证consumer线程在程序开始时首先获取互斥锁时,这种方法才有效。如果不是这种情况,死锁将作为第一个资源发生。生产者线程的信号将在使用者线程尚未等待资源时发送。因此,第一个resource.signal
基本上不会做任何事情,因此最终会出现这样一个场景:调用resource.signal
4次(当第一个丢失时),而调用resource.wait
5次。这意味着消费者将永远等待,并发生死锁
幸运的是,如果没有更多的即时工作可用,我们可以通过只允许使用者线程开始等待来解决这个问题
require 'thread'
queue = []
mutex = Mutex.new
resource = ConditionVariable.new
threads = []
threads << Thread.new do
5.times do |i|
mutex.synchronize do
if queue.empty?
resource.wait(mutex)
end
value = queue.pop
print "consumed #{value}\n"
end
end
end
threads << Thread.new do
5.times do |i|
mutex.synchronize do
queue << i
print "#{i} produced\n"
resource.signal
end
sleep(1) #simulate expense
end
end
threads.each(&:join)
需要“线程”
队列=[]
mutex=mutex.new
resource=ConditionVariable.new
线程=[]
线程queue.pop
是一个阻塞调用,你说得对。但是我可以删除队列.new
并用普通数组替换它
,问题保持不变,直到线程显然永远处于休眠状态,因此您无法调用join
。你可以在线程运行时使用类似于睡眠(1)的方法来绕过内置的死锁检测。any(&:alive?
@VictorMoroz)如果其中一个线程被停止,它将引发检测到的死锁,例如Thread.new{Thread.stop}.join
在我的例子中,除非你的意思是异常终止<线程中的代码>睡眠
不会导致死锁,至少我的Ruby(1.8.7)和MRI 1.9.3都不会,并且会导致死锁。您试图用条件变量解决的问题是什么?生产者-消费者问题IMO更容易用一个队列解决(除非你需要其他东西)。我还想调用打印
:)无论如何,你必须保证,生产者首先开始-这取决于系统@Stefan的解决方案有什么问题(只适用于我使用简单的线程。每个(&:join)
)?如果需要更多控制,您可以使用SizedQueue
。@Tombart Ruby已经在内部使用Mutex
为您处理同步。
require 'thread'
queue = Queue.new
threads = []
threads << Thread.new do
5.times do |i|
value = queue.pop
puts "consumed #{value}"
end
end
threads << Thread.new do
5.times do |i|
queue << i
puts "#{i} produced"
sleep(1) # simulate expense
end
end
# wait for the threads to finish
sleep(1) while threads.any?(&:alive?)
require 'monitor'
queue = []
queue.extend(MonitorMixin)
cond = queue.new_cond
consumers, producers = [], []
for i in 0..5
consumers << Thread.start(i) do |i|
print "consumer start #{i}\n"
while (producers.any?(&:alive?) || !queue.empty?)
queue.synchronize do
cond.wait_while { queue.empty? }
print "consumer #{i}: #{queue.shift}\n"
end
sleep(0.2) #simulate expense
end
end
end
for i in 0..3
producers << Thread.start(i) do |i|
id = (65+i).chr
for j in 0..10 do
queue.synchronize do
item = "#{j} #{id}"
queue << item
print "producer #{id}: produced #{item}\n"
j += 1
cond.broadcast
end
sleep(0.1) #simulate expense
end
end
end
sleep 0.1 while producers.any?(&:alive?)
sleep 0.1 while consumers.any?(&:alive?)
print "queue size #{queue.size}\n"
require 'thread'
queue = []
mutex = Mutex.new
resource = ConditionVariable.new
threads = []
threads << Thread.new do
5.times do |i|
mutex.synchronize do
if queue.empty?
resource.wait(mutex)
end
value = queue.pop
print "consumed #{value}\n"
end
end
end
threads << Thread.new do
5.times do |i|
mutex.synchronize do
queue << i
print "#{i} produced\n"
resource.signal
end
sleep(1) #simulate expense
end
end
threads.each(&:join)