【发布时间】:2013-06-18 17:34:57
【问题描述】:
用代码最容易解释:
require 'timeout'
puts "this block will properly kill the sleep after a second"
IO.popen("sleep 60") do |io|
begin
Timeout.timeout(1) do
while (line=io.gets) do
output += line
end
end
rescue Timeout::Error => ex
Process.kill 9, io.pid
puts "timed out: this block worked correctly"
end
end
puts "but this one blocks for >1 minute"
begin
pid = 0
Timeout.timeout(1) do
IO.popen("sleep 60") do |io|
pid = io.pid
while (line=io.gets) do
output += line
end
end
end
rescue Timeout::Error => ex
puts "timed out: the exception gets thrown, but much too late"
end
我对这两个模块的心智模型是相同的:
那么,我错过了什么?
编辑:drmaciver 在推特上建议,在第一种情况下,由于某种原因,管道套接字进入非阻塞模式,但在第二种情况下却没有。我想不出为什么会发生这种情况,也无法弄清楚如何获取描述符的标志,但这至少是一个合理的答案?正在研究这种可能性。
【问题讨论】:
-
你在运行哪个 ruby?
-
这种行为至少出现在 1.8.7 和 1.9.3 上。两个块上所有 60 个的 jruby 块,这是我事先猜到的行为。
-
请注意,我在两个块之间的
puts("but this one...")等到第一个sleep完成,因为第一个 IO#popen 块尽职尽责地调用waitpid()。如果你不想这样,那么你的救援逻辑需要杀死子进程。 -
@pilcrow 真正的代码确实会杀死 proc,但我为这段代码删除了它......我不应该这样做。将重新添加它,谢谢
-
@llimllib Timeout 接受任意类作为第二个参数来覆盖默认的 Timeout::Error。如果您插入自己的类来记录初始化时的系统时间,您应该能够确定您的问题是由于 Timeout 尝试引发的时间,还是由于 Ruby VM 在子进程上下文中传播引发的异常的方式。不是答案,但也许是通往答案的道路。
标签: ruby multithreading io timeout subprocess