ruby:在引发中断之前完成循环迭代

ruby: finish loop iteration before raising Interrupt

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

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

这是我的确切问题,我需要它来完成循环(睡眠,中途打印,睡眠,打印完成)然后才中断(包装 puts,睡眠......在一个方法中无济于事)

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

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

[-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执行,无论是否发生除零异常。

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

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