結城せんせいの「マルチスレッド本」を参考書にし、これをRubyで解釈する。
Rubyもデザインパターンも両方覚えちゃおうという強欲な試み。
Single Threaded Execution
壊れてしまう例
#!/usr/bin/env ruby #Single Threaded Execution #これは壊れる例 require "thread" class Gate def initialize @counter = 0 end def go_through name , address @name = name @address = address @counter = @counter + 1 self.check end def check if @name != @address then puts "BROKEN ** " + self.to_s end end def to_s return "No. #{@counter} : #{@name} , #{@address} " end end gate = Gate.new class UserThread < Thread def initialize(myname,myaddress,gate) @myaddress = myaddress @myname = myname @gate = gate block = Proc.new{ while(true) do #selfの@参照は、このインスタンス @gate.go_through @myname, @myaddress end } super(&block) end end threads = ("A".."C").map{|i| UserThread.new(i,i,gate) } threads.each{|t| t.join} puts "end"
これを実行すると壊れる。
$ > ruby thread03-1.rb BROKEN ** No. 206 : B , A BROKEN ** No. 232 : A , C BROKEN ** No. 1044 : A , C BROKEN ** No. 1148 : A , C BROKEN ** No. 1174 : C , B
コレは壊れない例。
#!/usr/bin/env ruby #Single Threaded Execution #これは壊れない例 require "thread" class Gate def initialize @m = Mutex.new @counter = 0 end def go_through name , address @m.synchronize{ #ロックを取る @name = name @address = address @counter = @counter + 1 self.check } #ロックを解除する。 end def check if @name != @address then puts "BROKEN ** " + self.to_s end end def to_s return "No. #{@counter} : #{@name} , #{@address} " end end gate = Gate.new class UserThread < Thread def initialize(myname,myaddress,gate) @myaddress = myaddress @myname = myname @gate = gate block = Proc.new{ while(true) do #selfの@参照は、このインスタンス @gate.go_through @myname, @myaddress end } super(&block) end end threads = ("A".."C").map{|i| UserThread.new(i,i,gate) } threads.each{|t| t.join} puts "end"
これを実行すると壊れない。
$ > ruby thread03-1.rb #ナニも起きない。Ctrl+Cで終了
Mutexについて
lock mutex オブジェクトをロックします。一度に一つのス レッドだけが mutex をロックできます。既にロックさ れている mutex に対してロックを行おうとしたスレッド は mutex のロックが解放されるまで、実行が停止されます。
mutextの使用例
Javaのsyncの替わりにmutex単位でロックを取ります。Javaはオブジェクトを守ってSynchronizeする。RubyはMutex単位になる。Javaの様にObjectのWaitで実行待ちをすることもない。この例だと、Mutex単位で待つ
def go_through name, address @m.synchronize{ #ロックを取る @name = name @address = address @counter = @counter + 1 self.check } #ロックを解除する。
Threadの継承
Javaのようなrun/startで管理されないRubyのスレッド。スレッドの動作させるにはブロックを作ってそこを実行する。
block=Proc.new{ do_something } t=Thread.new(&block)
結城浩せんせいのJavaマルチスレッド本に合わせて、敢えて、スレッドを継承して書いています。僕は次のようなパターンで書くことにしました。
thread継承の例:javaのコードを書き起こすとき
class MyThread < Thread def initialize block=Proc.new{ do_something } super(&block) end end thread=MyThread.new()
startを使う例:javaのコードを書き起こすとき
class MyThread def start block=Proc.new{ do_something } Thread.new(&block) end end thread=MyThread.new().start