0

我正在使用Timeout::timeout(1)一个需要超过 1 秒的过程,尽管它只是偶尔触发超时。当它发生时,rescue每次都以不同的方式捕获它。这是我的代码示例:

require 'timeout'
...
begin
  status = Timeout::timeout(1) {
    open(file_url) do |foo|
      feed = RSS::Parser.parse(foo)
      some_method_call(arg1, arg2)
      #other stuff
    end
  }
rescue Timeout::Error
  Rails.logger.debug "Timeout"
  return nil
rescue Exception => ex
  Rails.logger.debug "EXCEPTION - #{ex.message}"
  return nil
end

以下是我在相同输入下遇到的三种情况:

  • 流程运行完成,耗时超过 60 秒
  • execution expired进程超时并挂起,仅打印development.log
  • 进程超时,被正确抢救,打印"Timeout"development.log返回nil

为什么这如此不一致?

更新

将超时时间减少到 0.0001 秒后,该过程会始终如预期地超时。似乎该open(file_url)块的打开速度超过 1 秒,尽管该块内的所有内容都花费了超过 1 秒,但Timeout只有在打开本身花费的时间超过 1 秒时才会触发。

然而,这并没有解释execution expired异常。为了测试这一点,我将 移至块Timeout::timeout(0.0001)内。open代码如下所示:

require 'timeout'
...
begin
  open(file_url) do |foo|
    status = Timeout::timeout(0.0001) do
      begin
        feed = RSS::Parser.parse(foo)
        some_method_call(arg1, arg2)
        #other stuff
      rescue Timeout::Error
        Rails.logger.debug "Timeout 2"
      rescue Exception => ex
        Rails.logger.debug "EXCEPTION 2 - #{ex.message}"
      end
    end
  end
rescue Timeout::Error
  Rails.logger.debug "Timeout"
  return nil
rescue Exception => ex
  Rails.logger.debug "EXCEPTION - #{ex.message}"
  return nil
end

现在,我一直在收到输出EXCEPTION 2 - execution expired。为什么Timeout::Error这里没有被触发?

4

2 回答 2

1

你的内心

rescue Exception => ex
  Rails.logger.debug "EXCEPTION 2 - #{ex.message}"
end

防止外timeout块升起Timeout::Error

删除该rescue语句应该可以解决问题。

如果您确实需要捕获任何异常,请将其替换为:

rescue StandardError => ex
  Rails.logger.debug "EXCEPTION 2 - #{ex.message}"
end
于 2012-07-11T19:43:36.733 回答
0

在内部(在 Timeout 块内)Timeout 不使用 Timeout::Error。如果是这样,那么每个普通的救援都会抓住它,而你不希望这样。所以它创建了一个新的异常并使用它,这样它就有希望通过所有正常的错误处理并实际上使代码停止运行。

查看 ruby​​200/lib/ruby/2.0.0 中的 timeout.rb 代码。它很短,而且信息量很大。

特别是,您可以将自己的异常作为 Timeout::timeout 的第二个参数传入,Timeout 将使用它。因此,如果需要,您可以在代码中捕获它。

请注意,Logger 当前捕获写入时发生的所有异常,并且不会重新引发,因此它会中断 Timeout。我已经提交了错误报告。

于 2013-11-16T04:31:43.640 回答