Thread#valueでFutureパターン
Futureパターンは、時間のかかる処理の非同期実行と処理完了の待ち合わせをスマートに行うためのパターンです。
- 非同期スレッドの開始や待ち合わせ処理を、非同期実行を開始する関数と引換券に隠蔽し、
- 呼び出し側や時間のかかる処理でスレッドを意識する必要なしに、処理を非同期実行できるようにします。
RubyではThread#valueを使うとさくっと作れます。
# 処理の実行結果を得るための引換券 class Future def initialize( t ) @t = t end # 処理の実行結果を得る。 # - 処理が実行完了していなければ実行完了を待ち、結果を返す。 # - 実行完了していれば、即座に結果を返す。 # - 処理の途中で例外となっていれば、例外をスロー def get_result @t.value end end # 指定したブロックを非同期実行し、 # 結果を取得するための引換券を返す。 def do_asynch ( &block ) t = Thread.new &block return Future.new(t) end
利用サンプルです。
- do_asynchで非同期実行を開始し、Future#get_resultで結果を取得します。
- Future#get_result呼び出し時に処理が終了していなければ完了するまで待たされます。
- 非同期実行する処理や呼び出し側のサンプルでスレッドを意識していないのがポイント。
result = do_asynch { 10.times { |i| # 5秒待って結果を返す。 puts ".. " << i.to_s sleep 0.5 } "end" } puts "wait start." # 引換券から値を取得。 # 処理の完了までブロックされる。 puts result.get_result # 処理終了後は即座に結果が返る result = do_asynch { "end" } sleep 1 puts result.get_result # 処理の途中で例外が発生した場合、それをスロー result = do_asynch { raise "test" } puts result.get_result
実行結果です。
.. 0 wait start. .. 1 .. 2 .. 3 .. 4 .. 5 .. 6 .. 7 .. 8 .. 9 end end xxx/future.rb:49: test (RuntimeError) from xxx/future.rb:14:in `value' from xxx/future.rb:14:in `get_result' from xxx/future.rb:51
というか。
Futureでラップする必要はなかったり。呼び出し側でスレッドが出てくるけど、むしろ変に隠蔽されない方がわかりやすい!、という人はこれで。
result = Thread.fork { 10.times { |i| # 5秒待って結果を返す。 puts ".. " << i.to_s sleep 0.5 } "end" } puts "wait start." puts result.value
実行結果です。
.. 0 wait start. .. 1 .. 2 .. 3 .. 4 .. 5 .. 6 .. 7 .. 8 .. 9 end