I am looking for a solution of classic problem of exception handling. Consider following piece of code:
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
This code catches the exception after 5 seconds.
But if I change the array as [15, 5, 20, 3]
, above code catch the exception after 15 seconds. In short, it always catch the exception raised in first thread.
Any idea, why so. Why doesn't it catch the exception after 3 seconds each time? How do I catch the first raised exception by any thread?
Ruby also provides a separate class for an exception that is known as an Exception class which contains different types of methods. The code in which an exception is raised, is enclosed between the begin/end block, so you can use a rescue clause to handle this type of exception.
Ruby actually gives you the power to manually raise exceptions yourself by calling Kernel#raise. This allows you to choose what type of exception to raise and even set your own error message. If you do not specify what type of exception to raise, Ruby will default to RuntimeError (a subclass of StandardError ).
Exception handling in Thread : By default run() method doesn't throw any exception, so all checked exceptions inside the run method has to be caught and handled there only and for runtime exceptions we can use UncaughtExceptionHandler.
When you raise an exception in Ruby, the world stops and your program starts to shut down. If nothing stops the process, your program will eventually exit with an error message.
If you want any unhandled exception in any thread to cause the interpreter to exit, you need to set Thread::abort_on_exception= to true
. Unhandled exception cause the thread to stop running. If you don't set this variable to true, exception will only be raised when you call Thread#join
or Thread#value
for the thread. If set to true it will be raised when it occurs and will propagate to the main thread.
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
Output:
for 5 for 20 for 3 for 15 EXCEPTION: #<RuntimeError: after 3> MESSAGE: after 3
Note: but if you want any particular thread instance to raise exception this way there are similar abort_on_exception= Thread instance method:
t = Thread.new { # do something and raise exception } t.abort_on_exception = true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With