ruby – popen中的超时工作,但popen里面的一个超时没有?

前端之家收集整理的这篇文章主要介绍了ruby – popen中的超时工作,但popen里面的一个超时没有?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
代码中最容易解释:
  1. require 'timeout'
  2.  
  3. puts "this block will properly kill the sleep after a second"
  4.  
  5. IO.popen("sleep 60") do |io|
  6. begin
  7. Timeout.timeout(1) do
  8. while (line=io.gets) do
  9. output += line
  10. end
  11. end
  12. rescue Timeout::Error => ex
  13. Process.kill 9,io.pid
  14. puts "timed out: this block worked correctly"
  15. end
  16. end
  17.  
  18. puts "but this one blocks for >1 minute"
  19.  
  20. begin
  21. pid = 0
  22. Timeout.timeout(1) do
  23. IO.popen("sleep 60") do |io|
  24. pid = io.pid
  25. while (line=io.gets) do
  26. output += line
  27. end
  28. end
  29. end
  30. rescue Timeout::Error => ex
  31. puts "timed out: the exception gets thrown,but much too late"
  32. end

我的两个模块的心理模型是一样的:

那么,我失踪了什么?

编辑:drmaciver在twitter上建议,在第一种情况下,由于某些原因,管道插座进入非阻塞模式,但在第二种情况下.我不能想到为什么会发生这种情况,也不能弄清楚如何获得描述符的标志,但至少是一个合理的答案?努力解决这种可能性.

解决方法

阿哈,微妙.

在第二种情况下,在IO#popen块的末尾有一个隐藏的,阻塞的保证子句. Timeout :: Error被及时提升,但是在执行从该隐式确认子句返回之前,您无法挽救它.

Under the hood,IO.popen(cmd){| io | …}这样做:

  1. def my_illustrative_io_popen(cmd,&block)
  2. begin
  3. pio = IO.popen(cmd)
  4. block.call(pio) # This *is* interrupted...
  5. ensure
  6. pio.close # ...but then control goes here,which blocks on cmd's termination
  7. end

而IO#close调用实际上是一个或多或少的pclose(3),它阻止你进入waitpid(2)直到睡着的孩子退出.

你可以这样验证:

  1. #!/usr/bin/env ruby
  2.  
  3. require 'timeout'
  4.  
  5. BEGIN { $BASETIME = Time.now.to_i }
  6.  
  7. def xputs(msg)
  8. puts "%4.2f: %s" % [(Time.now.to_f - $BASETIME),msg]
  9. end
  10.  
  11. begin
  12. Timeout.timeout(3) do
  13. begin
  14. xputs "popen(sleep 10)"
  15. pio = IO.popen("sleep 10")
  16. sleep 100 # or loop over pio.gets or whatever
  17. ensure
  18. xputs "Entering ensure block"
  19. #Process.kill 9,pio.pid # <--- This would solve your problem!
  20. pio.close
  21. xputs "Leaving ensure block"
  22. end
  23. end
  24. rescue Timeout::Error => ex
  25. xputs "rescuing: #{ex}"
  26. end

所以,你可以做什么?

您必须以显式的方式执行此操作,因为解释器不会公开一种方法来覆盖IO#popen确认逻辑.您可以使用上述代码作为起始模板,并取消注释kill()行,例如.

猜你在找的Ruby相关文章