gpt4 book ai didi

ruby - 是什么导致我的 Ruby `trap` block 出现这种死锁?

转载 作者:数据小太阳 更新时间:2023-10-29 06:53:04 27 4
gpt4 key购买 nike

我正在通读 Jesse Storimer 的优秀著作,Working with Unix Processes .在有关从已退出的子进程捕获信号的部分中,他提供了一个代码示例。

我稍微修改了该代码(见下文)以更清楚地了解正在发生的事情:

  • 父级在信号之间恢复自己的执行(我可以通过它的 puts 看到),
  • wait 在一个 trap 语句中为多个 child 执行(有时我得到“收到 CHLD 信号”,然后是多个“ child pid 退出”)。

预期输出

通常下面代码的输出类似于:

parent is working hard
Received a CHLD signal
child pid 73408 exited
parent is working hard
parent is working hard
parent is working hard
Received a CHLD signal
child pid 73410 exited
child pid 73409 exited
All children exited - parent exiting too.

偶尔的错误

但偶尔我会收到这样的错误:

trapping_signals.rb:17:in `write': deadlock; recursive locking (ThreadError)
from trapping_signals.rb:17:in `puts'
from trapping_signals.rb:17:in `puts'
from trapping_signals.rb:17:in `block in <main>'
from trapping_signals.rb:17:in `call'
from trapping_signals.rb:17:in `write'
from trapping_signals.rb:17:in `puts'
from trapping_signals.rb:17:in `puts'
from trapping_signals.rb:17:in `block in <main>'
from trapping_signals.rb:40:in `call'
from trapping_signals.rb:40:in `sleep'
from trapping_signals.rb:40:in `block in <main>'
from trapping_signals.rb:38:in `loop'
from trapping_signals.rb:38:in `<main>

谁能给我解释一下这里出了什么问题?

代码

child_processes = 3
dead_processes = 0

# We fork 3 child processes.
child_processes.times do
fork do
# Each sleeps between 0 and 5 seconds
sleep rand(5)
end
end

# Our parent process will be busy doing some work.
# But still wants to know when one of its children exits.

# By trapping the :CHLD signal our process will be notified by the kernel
# when one of its children exits.
trap(:CHLD) do
puts "Received a CHLD signal"
# Since Process.wait queues up any data that it has for us we can ask for it
# here, since we know that one of our child processes has exited.

# We loop over a non-blocking Process.wait to ensure that any dead child
# processes are accounted for.
# Here we wait without blocking.
while pid = Process.wait(-1, Process::WNOHANG)
puts "child pid #{pid} exited"
dead_processes += 1

# We exit ourselves once all the child processes are accounted for.
if dead_processes == child_processes
puts "All children exited - parent exiting too."
exit
end
end
end

# Work it.
loop do
puts "parent is working hard"
sleep 1
end

最佳答案

我浏览了 Ruby sources查看引发该特定错误的位置,并且仅在当前线程尝试获取锁时才会引发该错误,但当前线程已占用该锁。这意味着锁定不可重入:

m = Mutex.new
m.lock
m.lock #=> same error as yours

现在至少我们知道会发生什么,但还不知道原因和地点。错误消息表明它发生在调用 puts 期间。当它被调用时,它最终以 io_binwrite 结束。 . stdout 未同步,但已缓冲,因此 this if condition在第一次调用时完成,并为该缓冲区设置一个缓冲区和一个写锁。写锁对于保证写入 stdout 的原子性很重要,不应发生两个同时写入 stdout 的线程混淆彼此的输出。为了证明我的意思:

t1 = Thread.new { 100.times { print "aaaaa" } }
t2 = Thread.new { 100.times { print "bbbbb" } }
t1.join
t2.join

虽然两个线程轮流写入stdout,但永远不会发生单次写入中断的情况——您将始终按顺序获得完整的 5 个 a 或 b。这就是写锁is there for .

现在你的情况出了问题是写锁上的竞争条件。父进程循环并每秒写入 stdout(“父进程正在努力工作”)。但同一个线程最终也执行了 trap block 并再次尝试写入 stdout(“收到 CHLD 信号”)。您可以通过在 puts 语句中添加 #{Thread.current} 来验证它是否真的是同一个线程。如果这两个事件发生得足够近,那么您将遇到与第一个示例相同的情况:同一个线程两次尝试获取同一个锁,这最终会触发错误。

关于ruby - 是什么导致我的 Ruby `trap` block 出现这种死锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10655764/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com