Edited at

Parallel 的ゾンビを殺した話


TL; DR

v1.16.0 から、 parallel の gem 使ってるときに timeout とか使って親を殺しても、ちゃんと子供達も kill されるようになりました。


詳細

parallel の gem は、便利なんだけれども、親スレッドを kill してもその子供達は普通に活動し続ける。 thread であろうとも process であろうとも。

例:

t = Thread.new do

Parallel.map(0..1000, in_threads: 2) do |i|
p i
sleep 1
end
end
sleep 1
t.kill
sleep
# 0
# 1
# 3
# 2
# 5
# 4
# => kill しているにも関わらず 1000 まで出力される。

たとえば、悪名高い timeout と組み合わせるとこうなる。

begin

Timeout.timeout(1) do
Parallel.map(0..1000, in_threads: 2) do |i|
p i
sleep 1
end
end
rescue Timeout::Error
p :timeout
end
sleep
# 0
# 1
# 2
# 3
# :timeout
# 4
# 5
# 6
# 7
# 8
# => 1000 まで続く

これは、あんまりにもあれなので、 parallel 本家に以下の PR を出してマージしてもらった。

https://github.com/grosser/parallel/pull/248

これによって、 Timeout やら 親スレッドの kill やらが行われた時に、その子供のスレッドやプロセスについても正しく kill されるようになった。

begin

Timeout.timeout(1) do
Parallel.map(0..1000, in_threads: 2) do |i|
p i
sleep 1
end
end
rescue Timeout::Error
p :timeout
end
sleep
# 0
# 1
# 3
# :timeout
# 2
# => ここでとまる

Concurrent Ruby を使えよ、という話は若干ある