这里有一些拼图。
首先,程序只等待主线程完成:
Thread.new { Thread.current.abort_on_exception = true; raise 'Oh, no!' }
puts 'Ready or not, here I come'
以上可能会或可能不会引发错误。
其次,如果您加入一个线程,则该线程引发的异常会被加入的线程从#join 方法重新引发:
gollum = Thread.new { raise 'My precious!!!' }
begin
gollum.join
rescue => e
# Prints 'My precious!!!'
puts e.message
end
此时,执行将返回到加入的线程。它不再连接到导致错误的线程或任何其他线程。它没有加入其他线程的原因是因为您一次只能加入一个线程。 threads.each(&:join) 实际上将你加入第一个,当它结束时 - 加入第二个等等:
frodo = Thread.new { raise 'Oh, no, Frodo!' }
sam = Thread.new { raise 'Oh, no, Sam!' }
begin
[frodo, sam].each(&:join)
rescue => e
puts e.message
end
puts 'This is the end, my only friend, the end.'
以上印刷品
哦,不,佛罗多!
这是结束,我唯一的朋友,结束。
现在让我们把它放在一起:
frodo = Thread.new { Thread.current.abort_on_exception = true; raise 'Oh, no, Frodo!' }
sam = Thread.new { Thread.current.abort_on_exception = true; raise 'Oh, no, Sam!' }
begin
[frodo, sam].each(&:join)
rescue => e
puts e.message
end
puts 'This is the end, my only friend, the end.'
这里可以发生很多事情。重要的是,如果我们设法加入(在此之前我们没有收到错误),救援将在主线程中捕获异常,无论哪个线程首先设法引发异常,然后在救援后继续。之后,主线程(以及程序)可能会或可能不会在其他线程引发其异常之前完成。
让我们检查一些可能的输出:
ex.rb:1:in `block in ':哦,不,佛罗多! (运行时错误)
Frodo 在我们加入之前提出了他的例外。
哦,不,山姆!
这是结束,我唯一的朋友,结束。
我们加入后,Sam 是第一个提出错误的人。在我们在主线程中打印了错误信息之后,我们也打印了结尾。然后主线程完成,在佛罗多提出他的错误之前。
哦,不,Frodo!ex.rb:2:in `block in':哦,不,Sam! (运行时错误)
我们设法加入了。佛罗多是第一个提出的,我们救了出来并打印了。在我们打印完结尾之前,Sam 提出了。
哦,不,山姆!
这是结束,我唯一的朋友,end.ex.rb:1:in `block in':哦,不,佛罗多! (运行时错误)
(很少)我们设法进行了救援。 Sam 首先提出了一个错误,我们从主线程打印了它。我们打印了结尾。就在打印之后,但在主线程终止之前,佛罗多也设法纠正了他的错误。
至于可能的解决方案,您只需要尽可能多的救援,因为有可能引发的线程。请注意,我还将线程创建放在受保护的块中,以确保我们在加入之前也能捕捉到潜在的错误:
def execute_safely_concurrently(number_of_threads, &work)
return if number_of_threads.zero?
begin
Thread.new(&work).join
rescue => e
puts e
end
execute_safely_concurrently(number_of_threads.pred, &work)
end
execute_safely_concurrently(2) do
Thread.current.abort_on_exception = true
raise 'Handle me, bitte!'
end