我正在寻找一个经典的异常处理问题的解决方案。请考虑以下代码:
def foo(n)
puts " for #{n}"
sleep n
raise "after #{n}"
end
begin
threads = []
[5, 15, 20, 3].each do |i|
threads << Thread.new do
foo(i)
end
end
threads.each(&:join)
rescue Exception => e
puts "EXCEPTION: #{e.inspect}"
puts "MESSAGE: #{e.message}"
end
此代码在5秒后捕获异常。
但是如果我将数组更改为[15, 5, 20, 3]
,则上面的代码会在15秒后捕获异常。简而言之,它始终捕获第一个线程中引发的异常。
任何想法,为什么如此。为什么不在每次3秒后捕获异常?如何通过任何线程捕获第一个引发的异常?
答案 0 :(得分:57)
如果您希望任何线程中的任何未处理的异常导致解释器退出,您需要将Thread::abort_on_exception=设置为true
。未处理的异常导致线程停止运行。如果未将此变量设置为true,则仅在为线程调用Thread#join
或Thread#value
时才会引发异常。如果设置为true,它将在出现时被引发并传播到主线程。
Thread.abort_on_exception=true # add this
def foo(n)
puts " for #{n}"
sleep n
raise "after #{n}"
end
begin
threads = []
[15, 5, 20, 3].each do |i|
threads << Thread.new do
foo(i)
end
end
threads.each(&:join)
rescue Exception => e
puts "EXCEPTION: #{e.inspect}"
puts "MESSAGE: #{e.message}"
end
输出:
for 5
for 20
for 3
for 15
EXCEPTION: #<RuntimeError: after 3>
MESSAGE: after 3
注意:但是如果您希望任何特定的线程实例以这种方式引发异常,那么类似abort_on_exception= Thread instance method:
t = Thread.new {
# do something and raise exception
}
t.abort_on_exception = true
答案 1 :(得分:6)
Thread.class_eval do
alias_method :initialize_without_exception_bubbling, :initialize
def initialize(*args, &block)
initialize_without_exception_bubbling(*args) {
begin
block.call
rescue Exception => e
Thread.main.raise e
end
}
end
end
答案 2 :(得分:0)
推迟异常处理(灵感来自@Jason Ling)
Set objCollection = IE.Document.getElementsByClassName("Class")
MsgBox objCollection.Length
答案 3 :(得分:0)
This will wait for the first thread to either raise or return (and re-raise):
require 'thwait'
def wait_for_first_block_to_complete(*blocks)
threads = blocks.map do |block|
Thread.new do
block.call
rescue StandardError
$!
end
end
waiter = ThreadsWait.new(*threads)
value = waiter.next_wait.value
threads.each(&:kill)
raise value if value.is_a?(StandardError)
value
end