at_exitとは
RubyにはKernel#at_exit
というメソッドがあります。
これは、Rubyの処理の終了時に実行する処理を追加出来るようなメソッドで、使い方によっては非常に便利に使えるものです。
(よく調べてないですが結構色々なGemで使用されているらしいです)
Parallelとは
Rubyにおいてマルチプロセス/スレッドの処理を簡単に使えるようにしてくれるGemです。
Rubyで並列処理を行いたい場合はよく使うと思います。
今回起きちゃった問題
at_exit
とParallelによるマルチプロセス処理を同時に使ったことで、おかしな挙動が置きました。
具体的にはこんな感じ↓
at_exit do
puts "run at_exit!"
end
Parallel.each(1..4, in_processes: 4) do |num|
sleep num
puts "sleep done! #{num}"
end
このファイルを実行した結果が↓
$ ruby at_exit_and_multi_process_test.rb
sleep done! 1
run at_exit!
sleep done! 2
run at_exit!
sleep done! 3
run at_exit!
sleep done! 4
run at_exit!
run at_exit!
at_exit
が5回も走ってる!!
基本的にat_exit
を使用する場合、処理の最後に1回だけ走って欲しいという感じの使い方をすると思うのですが、これだとよろしくないですね。
原因はまあなんとなく察しがつくと思います。
at_exit
内の処理はrubyの処理の終了時にフックされるわけですが、その「rubyの処理の終了」が、各子プロセスで発生してしまい、親プロセスの状態をforkしている子プロセスでも同じat_exit
の処理が走ってしまったという話ですね。(多分)
さらに親プロセスの終了分まで含めて5回も走ってしまったわけです。
ちなみにマルチスレッド処理では問題なく動作し、結果は下記のようになります↓
$ ruby at_exit_and_multi_thread_test.rb
sleep done! 1
sleep done! 2
sleep done! 3
sleep done! 4
run at_exit!
多分at_exit
とParallelを同時に使用している人があまりいないのか、ググってもあまりヒットしなかったので記事にしてみました。
考えてみれば全然わかることではあるのですが、特に意識せずにやってしまって挙動がおかしくなってしまったので皆様ご注意を
参考
https://docs.ruby-lang.org/ja/latest/method/Kernel/m/at_exit.html
https://techracho.bpsinc.jp/hachi8833/2018_05_22/56171
https://qiita.com/sekido-ts/items/5dc9c88d877bb2a1df0b
https://qiita.com/Kohei909Otsuka/items/26be74de803d195b37bd