使用以下脚本
threads = [
Thread.new { Thread.current.abort_on_exception = true; raise 'err' },
Thread.new { Thread.current.abort_on_exception = true; raise 'err' },
]
begin
threads.each(&:join)
rescue RuntimeError
puts "Got Error"
end
有一半时间我得到预期的“Got Error”,退出0,另一半得到test.rb:3:在< main>‘中的块:err(RuntimeError).
不应该救援能够处理这个吗?如果没有什么可能是两个线程同时引发错误的替代解决方案?
我已经考虑过不使用abort_on_exception = true但是问题是如果第一个线程有,比如说在加注之前睡眠(10),第二个线程会立即出错,直到10秒钟才会被捕获(由于线程数组的顺序).
Ruby MRI版本:
ruby 2.4.0p0(2016-12-24修订版57164)[x86_64-darwin15]
任何想法将不胜感激.谢谢!
更新
jruby-9.1.6.0似乎没有这个问题.可能是因为它固有的线程安全性.它始终打印Got Error,没有任何异常.不幸的是,JRuby不适合我们.
最佳答案 这里有几个难题.
首先,程序只等待主线程完成:
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.'
以上打印
Oh, no, Frodo!
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 ‘: Oh, no, Frodo! (RuntimeError)
在我们加入之前,佛罗多提出了他的例外.
Oh, no, Sam!
This is the end, my only friend, the end.
我们加入后,Sam是第一个提出错误的人.在主线程中打印出错误消息后,我们也打印了结束.然后主线程完成,在Frodo可以提出他的错误之前.
Oh, no, Frodo!ex.rb:2:in `block in ‘: Oh, no, Sam! (RuntimeError)
我们成功加入.佛罗多是第一个筹集,我们获救和印刷. Sam在我们打印结束之前加注了.
Oh, no, Sam!
This is the end, my only friend, the end.ex.rb:1:in `block in ‘: Oh, no, Frodo! (RuntimeError)
(很少)我们设法得救了. Sam首先提出错误,然后从主线程中打印出来.我们打印了结束.在打印之后,但在主线程终止之前,Frodo也成功地抓住了他的错误.
至于一个可能的解决方案,你需要尽可能多的救援,因为有可能引起的线程.请注意,我还将线程创建放在受保护的块中,以确保我们在连接之前捕获潜在的错误:
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