交换例程共享互斥锁

时间:2014-05-15 02:26:41

标签: ruby multithreading mutex condition-variable

我有一个方法a,它在某个随机时间重复调用,触发方法b,它在一些随机时间之后完全执行并且在它自己的线程中。我想确保后续执行a等待b完成,这由当前执行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中的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应该完成。

我希望替代执行ba,从而替代地打印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

但这没有效果。


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

2 个答案:

答案 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)}