Parallelのマルチプロセスでハマったこと
マルチプロセスにおいて子プロセスは親プロセスの変数に影響を及ぼさない
まず、以下のようなマルチスレッドで実行されるコードをみましょう。
Rubyコード
multi_threads.rb
require 'parallel'
files = [1, 2, 3, 4, 5, 6, 7 ,8, 9 ,10 ,11, 12, 13]
@array = []
Parallel.each(files, :in_threads=>10) {|x|
@array.push(x)
}
puts @array
結果
[1, 2, 4, 3, 5, 7, 6, 8, 9, 11, 13, 12, 10]
上記のものをマルチプロセスで実行するようにしようとして、 :in_threads
のところを単純に :in_processes
に変更しました。そうすると。。
Rubyコード
multi_processes.rb
require 'parallel'
files = [1, 2, 3, 4, 5, 6, 7 ,8, 9 ,10 ,11, 12, 13]
@array = []
Parallel.each(files, :in_processes=>10) {|x|
@array.push(x)
}
puts @array
結果
[]
何にも出力されませんでした!!!なんだって!!!
理由
Parallelでは子プロセスをforkして子プロセス内でロジックを遂行していきますが、同じ変数であっても親プロセスと各子プロセスの変数はメモリ上別領域にあるためです。
以下のコードを実行して結果を見ればわかるように、子プロセスで累積した結果は、親プロセスに何の影響も及ぼしません。
コード
multi_processes_test.rb
require 'parallel'
files = [1, 2, 3, 4, 5, 6, 7 ,8, 9 ,10 ,11, 12, 13]
@array = []
puts "parent pid: #{$$}"
Parallel.each(files, :in_processes=>10) {|x|
puts "pid:#{$$} #{x} #{@array}"
@array.push(x)
}
puts "parent pid:#{$$} #{@array}"
結果
parent pid: 65100
pid:65101 1 []
pid:65102 2 []
pid:65104 3 []
pid:65106 4 []
pid:65101 5 [1]
pid:65110 7 []
pid:65103 8 []
pid:65104 9 [3]
pid:65102 12 [2]
pid:65106 13 [4]
pid:65105 10 []
pid:65107 11 []
pid:65109 6 []
parent pid:65100 []
対処方法
Parallel.map
を使用することです。
Rubyコード
improved_multi_processes.rb
require 'parallel'
files = [1, 2, 3, 4, 5, 6, 7 ,8, 9 ,10 ,11, 12, 13]
@array = []
@array = Parallel.map(files, :in_processes=>10) {|x|
x
}
puts @array
結果
[1, 2, 4, 3, 5, 7, 6, 8, 9, 11, 13, 12, 10]
来た!!
このロジックいける理由は、子プロセスが死ぬときに、その戻り値を親プロセスに伝え、親プロセスがそれを収集して結果を返すためだそうです。。(僕もロジックはみてないので自身はありませんwww)
以上です。