From: keiju / Rational.Com (石塚圭樹) > >|ところで、スレッドセーフになるための要件にはどんなものが > >|あるでしょうか ? > >|今度なにか作るときは気をつけなくては。 > > > >Ruby本には石塚さんによるスレッドの競合を避けるための丁寧な解 > >説が載ってます. > > 丁寧って言えば丁寧かもしれませんが, 要件ってなっているかと言えばそうじゃ > ないような... > > # 詳しくは出てのお楽しみと言うことで(^^;;; ERb では、eRubyスクリプトの印刷結果を文字列と取り出す機能を 持っていて、これをスレッドセーフにしたいと考えています。 現在の実装は $> に、write すると文字列に蓄えていくオブジェクトを入れ スクリプト終了時に文字列を返すようにしています。 グローバル変数 $> を変更するというと、きっとスレッドセーフに なれないと思うのですが、どうでしょうか? $> への代入自体を捉えてスレッド毎のコンテキスト(?)に保存しては‥と考え、 次のようなスクリプトを書いてみて試したのですが、 eRuby スクリプトに Thread があるとうまくできない気がします。 ほかになにか良い方法はあるでしょうか? # ruby-1.2 にはないけど callcc を使ったらどうにかなるかなぁ。 #!/usr/local/bin/ruby require 'thread' class DefOut def initialize(out) @default = out @writers = Hash.new end def write(s) out = @writers.fetch(Thread.current, @default) out.write(s) end def set(obj) @writers[Thread.current] = obj cleanup end def cleanup @writers.delete_if do |k, v| not k.alive? end end @defout = DefOut.new($>) $> = @defout @th = Thread.new { while true sleep 60 $>.cleanup end } trace_var('$>') do @defout.set($>) $> = @defout end end class StringIO def initialize(str='') @str = str.to_s end attr :str def write(str) @str.concat(str) end def as_stdout(safe=nil) t = Thread.new { out = StringIO.new $> = out $SAFE = safe if safe yield out.str } t.value end module_function :as_stdout end if __FILE__ == $0 tab = [] tab.push Thread.new { StringIO.as_stdout do sleep 2 print "abc\n" end } tab.push Thread.new { StringIO.as_stdout do sleep 1 print "123\n" end } tab.each do |t| $stderr.print t.value end end