From: Shin-ichiro Hara Date: 1999-06-04T16:23:24+09:00 Subject: [ruby-list:14809] Re: ConditionVariable (again) 原です。 mutex.rb があるにもかかわらず、現在の thread.rb に入っている CV のコードを元にして、正常に動作するものに書き直そうと思ったので すが、、、 -----------------------^ cv1.rb class ConditionVariable def initialize @waiters = [] end def wait(mutex) mutex.unlock Thread.critical = true @waiters.push(Thread.current) Thread.stop mutex.lock end def signal Thread.critical = true t = @waiters.shift Thread.critical = false t.run if t end def broadcast Thread.critical = true waiters0 = @waitors.dup @waiters.clear Thread.critical = false for t in waiters0 t.run end end end -----------------------$ cv1.rb となりました。少し解説すると、現在の thread.rb では def wait(mutex) mutex.unlock @waiters_mutex.synchronize { @waiters.push(Thread.current) } Thread.stop mutex.lock end ですが、これは例えば咳さんの TinyQueue でいえば、Thread.stop の前 に Thread.pass を書いただけで、dead lock したりします。これの理由 は前田さんが [ruby-list:14504] で言っているように stop してないう ちに run されてしまうからです。で、Thread.critical = true をそれ 以前に書かなければならないわけですが、synchronize にも unlock にも Thread.critical = false というコードが含まれますから、このままで は効果がありません。そこで cv1.rb の様になったのです。 ところが、cv1.rb でもやはり wait の mutex.unlock の後に「すき間」 があるので、ここに Thread.pass を書いたりすると dead lock を起こ したりします。そこで次のように改良します。 -----------------------^ cv2.rb def Thread.exclusive begin Thread.critical = true r = yield ensure Thread.critical = false end r end class Mutex def exclusive_unlock return unless @locked Thread.exclusive do t = @waiting.shift @locked = false t.wakeup if t yield end self end end class ConditionVariable def initialize @waiters = [] end def wait(mutex) mutex.exclusive_unlock do @waiters.push(Thread.current) Thread.stop end mutex.lock end def signal t = @waiters.shift t.run if t end def broadcast waitors0 = nil Thread.exclusive do waiters0 = @waitors.dup @waiters.clear end for t in waiters0 t.run end end end -----------------------$ cv2.rb これでうまく動くと思います。 #同じ事が monitor.rb でも問題になると思いますが、大丈夫なのかな。 signal の後、条件が変わってしまって、、、という [ruby-list:6735] 以降のスレッドで議論された問題は run を使う限りないと思うのです がどうでしょうか。