2015年5月18日月曜日

【RubyOnRails】Parallelを使って並列処理をしているときにraiseした場合の動作

単純処理を繰り返し行う場合、高速化のためにParallelで並列処理する。
ふと思ったんだけど、処理を並列で進めているときに1つがraiseしたらそのほかの処理はどうなるんだろう。もちろんほかの処理も打ち切られてほしいんだけど、まさかraiseした以外のスレッドの処理は継続ってことはないよなぁ。。ということで確認してみた。

以下のサンプルプログラムで確認する。


# coding:utf-8

ary = [ *1..100 ]

Parallel.each( ary, :in_threads => 4 ) do | num |

p "#{num}: pid=#{Process.pid}"
raise if num == 50

end



さて、結果を予想すると、

50が割り当てられたthreadがraiseするが、その他のthreadも処理を打ち切るので50以降はプリントされないはず。

で実行してみると。。


$ bin/rails r script/multi_thread.rb

(省略)
"36: pid=28589"
"37: pid=28589"
"38: pid=28589"
"39: pid=28589"
"40: pid=28589"
"41: pid=28589"
"42: pid=28589"
"43: pid=28589"
"44: pid=28589"
"45: pid=28589"
"46: pid=28589"
"47: pid=28589"
"48: pid=28589"
"49: pid=28589"
"50: pid=28589"
script/multi_thread.rb:12:in `block in ': unhandled exception
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:430:in `call'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:430:in `call_with_index'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:240:in `block (3 levels) in work_in_threads'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:439:in `with_instrumentation'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:239:in `block (2 levels) in work_in_threads'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:233:in `loop'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:233:in `block in work_in_threads'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:136:in `block (2 levels) in in_threads'

予想通り50で止まった。マルチスレッドは並列処理といってもひとつのプロセス内で処理を分けているのでraiseすればプロセス自体が打ち切られ該当スレッド以外も止まる。
(プリントされているPIDもすべて同じ。)

ではマルチプロセスならどうなるだろう。


multi_thread.rbのin_threadsをin_processesに変えてプログラムは出来上がり。

$ bin/rails r script/multi_process.rb

(省略)
"41: pid=26378"
"44: pid=26381"
"43: pid=26375"
"45: pid=26378"
"46: pid=26375"
"47: pid=26378"
"48: pid=26375"
"50: pid=26378"
"49: pid=26381"
"52: pid=26381"
"53: pid=26381"
"54: pid=26381"
"55: pid=26381"
"56: pid=26381"
"57: pid=26381"
"58: pid=26381"
"59: pid=26381"
"51: pid=26375"
script/multi_thread.rb:8:in `block in ': unhandled exception
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:438:in `call'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:438:in `call_with_index'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:342:in `process_incoming_jobs'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:324:in `block in worker'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:317:in `fork'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:317:in `worker'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:305:in `block in create_workers'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:304:in `each'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:304:in `create_workers'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:263:in `work_in_processes'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:193:in `map'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:154:in `each'

止まったがぴったり50ではない。プリントされる順も昇順ではなく若干ばらばら(PIDもみんな一緒ではない)。50を引き当てたプロセスがraiseしてもその他のプロセスはすぐには止まらない模様。raiseしたプロセスが親プロセスに伝え、親プロセスが各子プロセスを止めて。。。みたいになると思うのでタイムラグがあると思う。出力される順番もばらつくので順番どおりに処理されないといけないという時はそもそもマルチプロセスだとだめっぽい。


どちらにしても、並列処理でraiseしたときに、raiseした以外のプロセス・スレッドも処理を停止する(マルチプロセスの場合タイムラグあり)ということはわかった。

0 件のコメント:

コメントを投稿