首页 > 解决方案 > 为什么线程中分叉进程的存在会改变陷阱块内的睡眠行为?

问题描述

这按预期工作:

signals = %w[INT TERM]
signals.each do |signal|
  Signal.trap(signal) do
    puts "trapping the signal and sleeping for 5 seconds"
    sleep 5
    puts "done, exiting"
    exit
  end
end

sleep 100

这样做:

# ...signal trap code from above...

t = Thread.new{ sleep 10 }
t.join

这样做:

# ...signal trap code from above...

`sleep 10`

但是,这不会:

# ...signal trap code from above...

t = Thread.new{ `sleep 10` }
t.join

对于前三个,启动代码然后立即发送control-c结果在 ruby​​ 等待 5 秒后退出。

第四,启动代码,然后立即发送control-c结果,ruby 立即退出。令人惊奇的是puts,“trapping the...”和“done...”这两条消息都被打印出来了,但sleep 5它们之间的信息似乎被跳过了。

使用 terrapin 而不是反引号会产生另一个线索 - terrapin 抱怨子进程以非零状态退出。尝试打印此状态时,它什么也不打印,可能表明该进程被毫不客气地硬杀死。

因此,对于在不是主线程的线程中创建的子进程,ruby 似乎具有某种默认行为。我怀疑这可能是设计使然,但我找不到任何有关它的文档或讨论。

我也试过

t = Thread.new{ `ls -R /` }

而不是睡眠,以防万一与竞争睡眠实现有某种交互,相同的行为。

为什么会这样?

更多实验

这也按预期运行。它等待线程内的子进程完成 10 秒。所以,奇怪的行为只发生在信号陷阱的情况下。

thread = Thread.new { `sleep 10` }
thread.join

测试是否正在发生涉及子线程同时捕获信号的事情,无论是通过设计还是由于错误。但这表现如预期:

@main_thread = Thread.current.object_id
puts "main thread: #{@main_thread}"

signals = %w[INT TERM]
signals.each do |signal|
  Signal.trap(signal) do
    puts Thread.current.object_id
    next unless Thread.current.object_id == @main_thread
    puts "thread from trap: #{Thread.current.object_id}"
    puts "trapping the signal and sleeping for 5 seconds"
    sleep 5
    puts "done, exiting"
    exit
  end
end

标签: rubymultithreadingforkchild-process

解决方案


推荐阅读