Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 如何使用条件变量?_Ruby_Mutex_Deadlock_Race Condition_Producer Consumer - Fatal编程技术网

Ruby 如何使用条件变量?

Ruby 如何使用条件变量?,ruby,mutex,deadlock,race-condition,producer-consumer,Ruby,Mutex,Deadlock,Race Condition,Producer Consumer,Ruby中关于条件变量的资源不多,但大多数都是错误的。就像,或者——他们都可能陷入僵局 我们可以通过按给定顺序启动线程来解决这个问题,并且可能在线程之间放置一些睡眠,以强制执行同步。但这只是推迟了真正的问题 我将代码改写成经典的: 需要“线程” 队列=[] mutex=mutex.new resource=ConditionVariable.new 线程=[] 线程基于一个问题,我提出了一个可行的解决方案。它强制执行线程之间的交替,这并不理想。我们想要消费者和生产者的多线程是什么 queue =

Ruby中关于条件变量的资源不多,但大多数都是错误的。就像,或者——他们都可能陷入僵局

我们可以通过按给定顺序启动线程来解决这个问题,并且可能在线程之间放置一些
睡眠
,以强制执行同步。但这只是推迟了真正的问题

我将代码改写成经典的:

需要“线程”
队列=[]
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)