2012-01-19 24 views
5

Estoy trabajando en multi threading en ruby. El fragmento de código esPunto muerto en Ruby join()

threads_array = Array.new(num_of_threads) 
    1.upto(num_of_threads) do |i| 

    Thread.abort_on_exception = true 
     threads_array[i-1] = Thread.new { 
     catch(:exit) do 
      print "s #{i}" 
      user_id = nil 
      loop do 
      user_id = user_ids.pop() 
      if user_id == nil 
       print "a #{i}" 
       Thread.stop() 
      end 
      dosomething(user_id) 
      end 
     end 
     } 
    end 
    #puts "after thread" 
    threads_array.each {|thread| thread.join} 

No estoy usando cualquier bloqueo de exclusión mutua. Pero consigo callejón sin salida .. A continuación se presenta la salida del código anterior ..

s 2s 1s 11s 8s 6s 7s 10s 14s 16s 21s 24s 26s 5s 3s 19s 20s 23s 28s 4s 9s 12s 18s 22s 29s 30s 27s 13s 17s 15s 25a 4a 10a 3a 6a 21a 24a 16a 9a 18a 5a 28a 20a 2a 22a 11a 29a 8a 14a 23a 26a 1a 19a 7a 12fatal: punto muerto detectado

la salida anterior nos dice que estancamiento es después de la matriz user_ids es nula y pasando con join() y stop() de la clase Thread en ruby ​​... ¿Qué está pasando realmente y cuál es la solución a este error?

+0

fue la respuesta correcta? ¿Has resuelto el problema? –

Respuesta

19

El simples código para reproducir este problema es:

t = Thread.new { Thread.stop } 
t.join # => exception in `join': deadlock detected (fatal) 

Tema :: detener → nula

ejecución

paradas del hilo actual, ponerlo en un “sueño” Estado y programa la ejecución de otro hilo.

hilo # unen → Thr
hilo # unen (límite) → Thr

El subproceso de llamada suspenderá la ejecución y la gestión del thr. No devuelve hasta que termine o hasta que hayan transcurrido los límites. Si el límite de tiempo expira, se devolverá nil, de lo contrario se devuelve thr.

Por lo que yo entiendo que llaman Thread.join sin parámetros en la rosca y esperar a que salga, pero el hilo hijo llama Thread.stop y entra en estado de sleep. Esta es una situación deadloc: el hilo principal espera a que el subproceso secundario salga, pero el subproceso secundario está inactivo y no responde.

Si llama join con limit parámetro a continuación hilo hijo será abortado tras el tiempo límite sin causar estancamiento de su programa:

t = Thread.new { Thread.stop } 
t.join 1 # => Process finished with exit code 0 

recomendaría para salir de sus subprocesos de trabajo después de que hicieron el trabajo con Thread.exit o conseguir deshacerse de bucle infinito y llegar a final de hilo de ejecución, normalmente, por ejemplo:

if user_id == nil 
    raise StopIteration 
end 

#or 
if user_id == nil 
    Thread.exit 
end 
+0

Bonito artículo escrito; bien hecho. – Phrogz

+0

impresionante, gracias – glebm

0

Si consigo sus intenciones derecho que yo consideraría algo más simple (y probablemente más seguro, users_ids.pop() desde dentro hilo da miedo a mí):

user_ids = (0..19).to_a 
number_of_threads = 3 

user_ids \ 
    .each_slice(user_ids.length/number_of_threads + 1) \ 
    .map { |slice| 
     Thread.new(slice) { |s| 
     puts s.inspect 
     } 
    }.map(&:join) 
5

Además de la respuesta de Alex Kliuchnikau, voy a añadir que #join podría plantear este error cuando el hilo está esperando Queue#pop. Una solución simple y consciente es llamar al #join con un tiempo de espera excedido.

Esto es de ruby ​​2.2.2:

[27] pry(main)> q=Queue.new 
=> #<Thread::Queue:0x00000003a39848> 
[30] pry(main)> q << "asdggg" 
=> #<Thread::Queue:0x00000003a39848> 
[31] pry(main)> q << "as" 
=> #<Thread::Queue:0x00000003a39848> 
[32] pry(main)> t = Thread.new { 
[32] pry(main)* while s = q.pop 
[32] pry(main)*  puts s 
[32] pry(main)* end 
[32] pry(main)* } 
asdggg 
as 
=> #<Thread:[email protected](pry):34 sleep> 
[33] pry(main)> q << "asg" 
asg 
=> #<Thread::Queue:0x00000003a39848> 
[34] pry(main)> q << "ashg" 
ashg 
=> #<Thread::Queue:0x00000003a39848> 
[35] pry(main)> t.join 
fatal: No live threads left. Deadlock? 
from (pry):41:in `join' 
[36] pry(main)> t.join(5) 
=> nil 
+0

¿y si la cola es una conexión persistente http? como la transmisión en vivo, y el resultado en la transmisión es aleatorio, ¿todavía funciona el t.join (5)? –

+0

@crazy_phage, no he tenido este caso de uso, pero no veo por qué no debería funcionar. En el caso de una conexión HTTP persistente, supongo que está implementando un tiempo de espera después de la cual la conexión debe cerrarse, ¿correcto? Si quiere esperar para siempre, puede establecer un valor muy grande, como 10 años. – akostadinov

+0

bueno, es como si la conexión http fuera una tubería y tuviera otro hilo para leer de la tubería, si uso t.join no j.join 5, se bloqueará, pero no vi por qué sucede eso, porque funcionó eso en sidekiq, el registro no mostró nada. Entonces, acabo de ver su respuesta, y creo que es por eso que esto sucede. –

Cuestiones relacionadas