マルチスレッドデザインパターンについてまとめる。
今回はFutureパターン。
Futureパターンとは、実行結果の代わりに引換券をもらい、
のちのち(未来)に、その引換券を使って結果を得るスレッドパターンである。
例えば、ケーキ屋でケーキを注文し、引換券をもらう。
夕方、再度ケーキ屋に足を運び、引換券とケーキを交換してもらう。
そういった場合に利用する。
#!/usr/bin/ruby require 'thread' Thread.abort_on_exception = "yes" class Host # requestメソッド内で利用される引数やローカル変数は # このrequestメソッドを呼び出したスレッドに固有である # 複数のスレッドで共有されないためsynchronize処理は不要である def request(request_id) # FutureDataのインスタンスを処理結果を待たないで # returnするため、スレッド外で事前に作成しておく future_data = FutureData.new() Thread.start do real_data = RealData.new(); real_data.handle(request_id); future_data.setRealData(real_data); end puts("Request Post:" + request_id.to_s) return future_data end end class RealData def initialize() @result = nil end def handle(request_id) puts("Request handling:" + request_id.to_s) sleep(rand(5)) @result = "Work End:" + request_id.to_s end def getContent() return @result end end class FutureData def initialize() @is_ready = false @real_data = nil @mutex = Mutex.new @cv = ConditionVariable.new end def setRealData(real_data) @mutex.synchronize do # balkingパターン # 2回以上setRealDataが呼ばれないようにする if (@is_ready == true) return end @real_data = real_data @is_ready = true # FutureDataインスタンスごとに状態変数は固有である # よって再開処理はそのインスタンスのみに適用される # 状態変数を待っているスレッドがすべて再開されるわけではない @cv.broadcast end end def getContent() @mutex.synchronize do while (@is_ready == false) @cv.wait(@mutex) end return @real_data.getContent() end end end host = Host.new() data1 = host.request(1) data2 = host.request(2) data3 = host.request(3) t1 = Thread.start do puts(data1.getContent()) end t2 = Thread.start do puts(data2.getContent()) end t3 = Thread.start do puts(data3.getContent()) end t1.join t2.join t3.join
処理結果は下のようになる。
$ ruby future.rb Request handling:1 Request Post:1 Request handling:2 Request Post:2 Request handling:3 Request Post:3 Work End:2 Work End:3 Work End:1
実際にFutureパターンを利用した例(Active Objectパターン内でFutureパターンを利用している)をあげておく。
また、スレッドのデザインパターン以前の並行処理プログラミングの基礎はここを参考してほしい。