在代码中最容易解释:
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在twitter上建议,在第一种情况下,由于某些原因,管道插座进入非阻塞模式,但在第二种情况下.我不能想到为什么会发生这种情况,也不能弄清楚如何获得描述符的标志,但至少是一个合理的答案?努力解决这种可能性.
解决方法
阿哈,微妙.
在第二种情况下,在IO#popen块的末尾有一个隐藏的,阻塞的保证子句. Timeout :: Error被及时提升,但是在执行从该隐式确认子句返回之前,您无法挽救它.
Under the hood,IO.popen(cmd){| io | …}这样做:
def my_illustrative_io_popen(cmd,&block) begin pio = IO.popen(cmd) block.call(pio) # This *is* interrupted... ensure pio.close # ...but then control goes here,which blocks on cmd's termination end
而IO#close调用实际上是一个或多或少的pclose(3),它阻止你进入waitpid(2)直到睡着的孩子退出.
你可以这样验证:
#!/usr/bin/env ruby require 'timeout' BEGIN { $BASETIME = Time.Now.to_i } def xputs(msg) puts "%4.2f: %s" % [(Time.Now.to_f - $BASETIME),msg] end begin Timeout.timeout(3) do begin xputs "popen(sleep 10)" pio = IO.popen("sleep 10") sleep 100 # or loop over pio.gets or whatever ensure xputs "Entering ensure block" #Process.kill 9,pio.pid # <--- This would solve your problem! pio.close xputs "Leaving ensure block" end end rescue Timeout::Error => ex xputs "rescuing: #{ex}" end
所以,你可以做什么?
您必须以显式的方式执行此操作,因为解释器不会公开一种方法来覆盖IO#popen确认逻辑.您可以使用上述代码作为起始模板,并取消注释kill()行,例如.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。