From: Masatoshi SEKI Date: 2000-06-17T03:16:56+09:00 Subject: [ruby-list:23297] Re: synchronize or lock 咳といいます。 > > ちなみにこの例の場合,上記のブロック内で新たに導入されたローカル変数は > > ブロックローカル,というルールにより,s はスレッドローカルになります. > > だからそもそもここで相互排除を行う必要は無かったりして. > 訂正.やっぱり race condition になりそうですね. > > : > | > Socket#accept > | > Thread.new----------------+ > | | > Socket#accept <-------> s = ns <- ここで > | > : > s は確かにスレッドローカルだけど ns が accept で書き換えられる可能性が > ある.ということで確かに Mutex による相互排除あたりはあった方が良いの > かも.....それなら accept も lock/unlock で括らなきゃだめか. 自分のdrb.rbが心配になって見てみました。メインループの内側、 accept 1回分の仕事を一つのメソッドにしていました。つまり ns = gs.accept の ns も一回の処理ごとのローカルな変数とすることで、競合状態に ならないようにしてるっぽいです。 # 以前、もっとスマートな解決方法を見た覚えがあります。 そういえば、 Ruby本の 2.22.1 などのサンプルは危険そうに思います。 > まつもとさん OpenDesign の toy_http_server.rb も ?? > gotokenさん while TRUE ns = gs.accept ... end でなくて、 loop do ns = gs.accept ... end として ns をブロックローカルで作ればいいのかな。 # 実験したらwhile の中はブロックじゃないんですね。当り前か。 #---- こっちは止まるけど while TRUE i = i ? i + 1 : 0 puts i exit if i > 9 end #---- こっちは止まらない loop do i = i ? i + 1 : 0 puts i exit if i > 9 end と、ここまできて話がひっくり返るのですが … 本来は危険区間と思われるのですが、1.4.4 だと Thread.new の先頭で s = ns 方式 ってうまく動きますね。(偶然でしょうけど) 必ず最初の 1 primitive(?)は実行されてからスレッドがスイッチするような 仕組みがあるのだろうか…。 -- a.rb require 'thread' LOOP = 10000 q = Queue.new n = LOOP while TRUE ns = n # acceptのかわり Thread.start do s = ns # 完了する前にスイッチしないの?? q.push(s) end n -= 1 break if n <= 0 end LOOP.times do puts q.pop end -- % ruby a.rb | sort | uniq | wc 10000 10000 48894 ちゃんと動いていて期待を裏切ります。 これを s = ns やめて Thread.start do q.push(ns) # 完了する前にスイッチしないの?? end とすると… % ruby a.rb | sort | uniq | wc 9902 9902 48418 となって、期待通り(?)失敗してくれます。 最初の話にもどって、while TRUE を loop do と差し替え、 ns をブロックローカルとすることでちゃんと動作してくれました。 % ruby a.rb | sort | uniq | wc 10000 10000 48894