Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
是什么导致我的Ruby`trap`块出现这种死锁?_Ruby_Multithreading_Signals_Deadlock_Fork - Fatal编程技术网

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

是什么导致我的Ruby`trap`块出现这种死锁?,ruby,multithreading,signals,deadlock,fork,Ruby,Multithreading,Signals,Deadlock,Fork,我正在阅读杰西·斯托莱默的优秀著作。在关于捕获已退出的子进程的信号的一节中,他给出了一个代码示例 我稍微修改了代码(见下文),以便更清楚地了解正在发生的事情: 父级在信号之间恢复自己的执行(我可以通过它的put看到这一点) 在一个trap语句中对多个子项执行wait(有时我会得到一次“接收到一个CHLD信号”,然后是多个“子项pid exited”) 预期产量 通常,下面代码的输出类似于: 家长在努力工作 收到CHLD信号 子pid 73408已退出 父母在努力工作 父母在努力工作 父母在努

我正在阅读杰西·斯托莱默的优秀著作。在关于捕获已退出的子进程的信号的一节中,他给出了一个代码示例

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

  • 父级在信号之间恢复自己的执行(我可以通过它的
    put
    看到这一点)
  • 在一个
    trap
    语句中对多个子项执行
    wait
    (有时我会得到一次“接收到一个CHLD信号”,然后是多个“子项pid exited”)
预期产量 通常,下面代码的输出类似于:

家长在努力工作
收到CHLD信号
子pid 73408已退出
父母在努力工作
父母在努力工作
父母在努力工作
收到CHLD信号
子pid 73410已退出
子pid 73409已退出
所有子项都已退出-父项也已退出。
偶然的错误 但偶尔我会遇到这样的错误:

trapping_signals.rb:17:in'write':死锁;递归锁定(线程错误)
来自陷阱信号。rb:17:in'puts'
来自陷阱信号。rb:17:in'puts'
来自捕获信号。rb:17:in‘block in’
来自捕捉信号。rb:17:in'call'
从捕获信号。rb:17:in'write'
来自陷阱信号。rb:17:in'puts'
来自陷阱信号。rb:17:in'puts'
来自捕获信号。rb:17:in‘block in’
来自捕捉信号。rb:40:in'call'
来自捕捉信号。rb:40:in'sleep'
来自捕捉信号。rb:40:in‘block in’
来自捕捉信号。rb:38:in'loop'
从捕获信号。rb:38:in`
有人能解释一下这里出了什么问题吗

代码
子进程=3
死_进程=0
#我们分叉3个子进程。
儿童时代
叉子
#每个人的睡眠时间在0到5秒之间
睡眠(5)
结束
结束
#我们的父进程将忙于做一些工作。
#但仍然想知道它的一个孩子什么时候离开。
#通过捕获:CHLD信号,内核将通知我们的进程
#当它的一个孩子离开时。
陷阱(:CHLD)do
放入“收到CHLD信号”
#由于Process.wait将它为我们提供的任何数据排队,因此我们可以请求它
#这里,因为我们知道我们的一个子进程已经退出。
#我们在一个非阻塞进程上循环。等待以确保任何死亡的孩子
#对过程进行了说明。
#我们在这里等着,没有阻挡。
而pid=Process.wait(-1,Process::WNOHANG)
将“子pid#{pid}退出”
死_进程+=1
#一旦所有的子进程都被说明,我们就退出了。
如果死_进程==子_进程
将“所有子项退出-父项也退出”
出口
结束
结束
结束
#工作吧。
环道
“家长正在努力工作”
睡眠1
结束
我仔细查看了该特定错误是在哪里引发的,只有当当前线程试图获取锁时才会引发该错误,但当前线程已经获取了相同的锁。这意味着锁定不是重入的:

m = Mutex.new
m.lock
m.lock #=> same error as yours
现在至少我们知道发生了什么,但还不知道为什么和在哪里。错误消息表明,它发生在调用
put
期间。当它被调用时,它最终会被调用
stdout
是不同步的,但它是缓冲的,因此在第一次调用时会执行,并且会为该缓冲区设置一个缓冲区和一个写锁。写锁对于保证对stdout的写入的原子性很重要,不应该出现两个线程同时写入
stdout
时混淆彼此的输出的情况。为了证明我的意思:

t1 = Thread.new { 100.times { print "aaaaa" } }
t2 = Thread.new { 100.times { print "bbbbb" } }
t1.join
t2.join
虽然两个线程轮流向stdout写入,但不会发生一次写入中断的情况-您将始终按顺序拥有完整的5个a或b。这就是写锁


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

感谢@emboss:)我自己实际遇到了这一问题,并在上周的书中更新了代码示例。解决方案是设置
$stdout.sync=true
。这将使stdout同步,因此缓冲区的写锁上没有竞争条件,因为缓冲区被完全跳过(至少这是我的理解)。我们必须感谢您的好书@jessetorimer!是的,我也认为,同步
$stdout
可能是解决这个问题最优雅的方法。或者,在给定的上下文中,如果出于某种原因这不是选项,则使用某种形式的手动同步。