2012-02-01 11 views
22

Busco a una solución del problema clásico de manejo de excepciones. Considere la siguiente pieza del código:Manejo de excepciones planteadas en un hilo Rubí

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 

Este código captura la excepción después de 5 segundos.

Pero si cambio de la matriz como [15, 5, 20, 3], por encima de captura código de excepción después de 15 segundos. En resumen, siempre capta la excepción planteada en el primer hilo.

Cualquier idea, ¿por qué hacerlo. ¿Por qué no detecta la excepción después de 3 segundos cada vez? ¿Cómo atrapo la primera excepción planteada por cualquier hilo?

Respuesta

48

Si desea que cualquier excepción no controlada en cualquier hilo haga que el intérprete salga, debe establecer Thread::abort_on_exception= en true. La excepción no controlada hace que el hilo deje de ejecutarse. Si no establece esta variable en verdadero, la excepción solo se generará cuando llame al Thread#join o Thread#value para la cadena. Si se establece en verdadero, se levantará cuando ocurra y se propagará al hilo principal.

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 

Salida:

for 5 
for 20 
for 3 
for 15 
EXCEPTION: #<RuntimeError: after 3> 
MESSAGE: after 3 

Nota: pero si quieres cualquier instancia hilo en particular para elevar una excepción de esta manera no son similares abort_on_exception= Thread instance method:

t = Thread.new { 
    # do something and raise exception 
} 
t.abort_on_exception = true 
+0

gracias por su respuesta. Conocí el flag abort_on_exception. Pero mi requisito es saber cuál es el primer hilo para hacer una excepción y luego tomar algunas decisiones al respecto. –

+0

@AkashAgrawal, no obtengo tu último comentario. Capturará la primera excepción (de thread con sleep 3) en la cláusula 'rescue' y aquí puede tomar una decisión al respecto. Todos los subprocesos continuarán ejecutándose si su subproceso principal no saldrá después de la primera excepción. –

+0

Así que este era un código de prueba. Mi pregunta fue cómo voy a ver la primera excepción lanzada desde cualquier hilo. Cualquier hilo puede lanzar una excepción en cualquier momento. –

2
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 
+7

Votado porque es solo código sin explicaciones. Necesita comentarios, descripción de lo que hace, por qué, etc. –

+1

Además, no anule las clases de ruby ​​core. -1'd – Senjai

+0

No estoy de acuerdo con ambos comentarios. – Nakilon