原です。

In message "[ruby-list:14847] Re: ConditionVariable (again)"
    on 99/06/07, Shugo Maeda <shugo / netlab.co.jp> writes:
|
|前田です。

|> 結局、私のあの単純な CV のコード(cv2.rb)、あれはあれでちゃん
|> と動くんですよね。
|
|競合する(条件を偽にしようとする)スレッドにロックを横取りされる可
|能性はあります。

そうですね。だから、ユーザーの条件変数の一般的利用法としては、

mutex.synchronize {
  ...
  if ある条件
    cond_var.wait(mutex)
  end
  ...
}

ではなくて、スレッドにロックを横取りされることを前提に

mutex.synchronize {
  ...
  while ある条件
    cond_var.wait(mutex)
  end
  ...
}

と書くべきなんですね。せきさんのプログラムでは前者で十分だけど。


|ただ、私のコードでも、競合するスレッドで、
|
|1000.times do
|  break if m.try_lock
|  Thread.pass
|end
|
|などとされるとだめですね。

monitor.rb では、signal のスレッドから1周する間に競合するス
レッドの幾つかを止めて回るので、2周目、sinal のスレッドから
wait のスレッドに移動する間に邪魔される確率が減る、またそこ
でロックを横取りされても優先的に取り戻すので同じスレッドに2
度邪魔されることは殆んどない(1度はあるかもしれないが)、と
いう様なメカニズムでしょうか。

実験:

-----^ test1.rb
#!/usr/bin/ruby
Thread.abort_on_exception = true
require "monitor"

$mutex = Monitor.new
$full = $mutex.new_cond
$empty = $mutex.new_cond
$queue = []
$max = 1
$waste = 0
$waste1 = 0

3.times do
  Thread.new {
    n = 0
    loop do
      $mutex.synchronize {
	k = 0
	while $queue.size >= $max
	  k += 1
	  $full.wait
	  $waste += 1 if k > 1
	end
	$waste1 += 1 if k > 1
	$queue.push n
	$empty.signal if $queue.size >= 1 
      }
      n += 1
    end
  }
end

1000.times do
  $mutex.synchronize {
    while $queue.size <= 0
      $empty.wait
    end
    v = $queue.shift
    $full.signal if $queue.size < $max
  }
end

print $waste, ", ", $waste1, "\n"
-----$ test1.rb

これを実行すると $waste == $waste1 で一桁ぐらいです。
これを cv2.rb の ConditionVariable で書くと $waste1 が
数倍、$waste は数十倍。かなり違うものですね。