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
我正在循环很多项目,我想定期中断循环以保存并在以后继续,如下所示:
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