【问题标题】:ruby: finish loop iteration before raising Interruptruby:在引发中断之前完成循环迭代
【发布时间】:2017-11-29 06:16:45
【问题描述】:

我正在循环很多项目,我想定期中断循环以保存并在以后继续,如下所示:

begin
  big_list.each do |i|
    # sensitive stuff
    sensitive_method(i)
    # other sensitive stuff
  end
rescue Interrupt
  # finish the current iteration
  # then do something else (save)
  # don't raise (print done)
end

敏感是指,如果在迭代过程中引发Interrupt,数据将被破坏,因此我需要保证迭代在退出之前完成。

此外,如果引发另一个异常,它仍应完成循环但随后引发它

编辑:

在测试场景中使用 mudasobwa 的答案:

while true
  result = begin
             puts "start"
             sleep 1
             puts "halfway"
             sleep 1
             puts "done\n\n"
              nil
            rescue Exception => e
              e
            end
  case result
    when Interrupt
      puts "STOPPED"
      break
    when Exception then raise result
  end
end

我明白了:

start
halfway
done

start
^C: /...
STOPPED

这是我的确切问题,我需要它来完成循环(睡眠、打印中途、睡眠、打印完成)然后才爆发(包装放置、睡眠......在一个方法中没有帮助)

【问题讨论】:

  • 你可以在循环内移动begin..rescue
  • 你应该在 sensitive_method 中拯救 Interrupt 否则当你在循环之外拯救它时,你必须重新启动/调用该方法调用。然后从sensitive_method 中引发关闭错误/方法(假设一切都是同步的)。
  • @Anthony 好的,但是如果我中断,方法体不会中途停止执行,这就是我要避免的

标签: ruby loops exception error-handling interrupt


【解决方案1】:

TL;DR:无法从中间继续执行该方法。

big_list.each do |i|
  # sensitive stuff
  result = begin
             sensitive_method(i)
             nil
           rescue Exception => e
             e
           end
  # other sensitive stuff
  case result
    when Interrupt
      puts "done"
      break "done"
    when Exception then raise result
  end
end

旁注:你可能不想拯救最顶层的Exception,而是一些对拯救有意义的子类。


为了能够完成大量操作:

operations = [
  -> { puts "start" },
  -> { sleep 1 },
  -> { puts "halfway" },
  -> { sleep 1 },
  -> { puts "done\n\n" }
]

def safe_chunk(operations, index = 0)
  result = operations[index..-1].each_with_index(index) do |op, idx|
    begin
      op.()
    rescue Exception => e
      safe_chunk(operations, idx) # or idx + 1
      break e
    end
  end
  result.is_a?(Array) ? nil : result
end

【讨论】:

  • 上面的敏感方法是原子操作。如果您希望能够完成许多操作,您应该将sensitive_method 分解为 atomic 操作,然后手动重新运行该块。查看更新。
【解决方案2】:

rescue 子句中使用的关键字ensure 可用于这种情况,即发生异常后必须执行代码。

[-1, 0, 1].each do |i|
  begin
    puts "i=#{i} before exception"
    # <additional code>  
    n = 1/i
  rescue ZeroDivisionError => e
    puts "Exception: #{e}"
    exit
  ensure
    puts "Just executed 1/#{i}"
    # <additional code>  
  end
end

i=-1 before exception
Just executed 1/-1
i=0 before exception
Exception: divided by 0
Just executed 1/0

请注意begin/rescue/ensure/end 必须在循环内,并且ensure 之后的代码将针对每个i 执行,无论是否发生零除异常。

【讨论】:

    【解决方案3】:

    在主线程中引发了中断异常。如果您使用工作线程来处理列表,它将永远不会被中断。不过,您将需要一种方法来告诉工作线程终止。在主线程中抢救中断并设置一个由子线程检查的标志可以完成此操作。

    BigList = (1..100)
    
    def sensitive_method(item)
      puts "start #{item}"
      sleep 1
      puts "halfway #{item}"
      sleep 1
      puts "done #{item}"
      puts
    end
    
    @done = false
    
    thread = Thread.new do
      begin
        BigList.each do |item|
          break if @done
          sensitive_method item
        end
      end
    end
    
    begin
      thread.join
    rescue Interrupt
      @done = true
      thread.join
    end
    

    【讨论】:

      猜你喜欢
      • 2018-02-08
      • 2020-11-18
      • 2019-07-22
      • 1970-01-01
      • 2014-10-17
      • 1970-01-01
      • 2016-11-04
      • 1970-01-01
      • 2020-07-02
      相关资源
      最近更新 更多