¿Cómo manejo los hilos de Ruby para que terminen todo su trabajo?

Tengo un cálculo que se puede dividir en unidades independientes y la forma en que lo estoy tratando ahora es mediante la creación de un número fijo de subprocesos y luego la entrega de trozos de trabajo a realizar en cada subproceso. Así que en el pseudo código esto es lo que parece

# main thread work_units.take(10).each {|work_unit| spawn_thread_for work_unit} def spawn_thread_for(work) Thread.new do do_some work more_work = work_units.pop spawn_thread_for more_work unless more_work.nil? end end 

Básicamente, una vez que se crea el número inicial de subprocesos, cada uno hace un poco de trabajo y luego continúa tomando las cosas que deben hacerse desde la stack de trabajo hasta que no quede nada. Todo funciona bien cuando ejecuto cosas en irb, pero cuando ejecuto el script utilizando el intérprete, las cosas no funcionan tan bien. No estoy seguro de cómo hacer que el hilo principal espere hasta que todo el trabajo haya finalizado. ¿Hay una buena manera de hacer esto o estoy atascado con la ejecución del sleep 10 until work_units.empty? en el hilo principal

Si modifica spawn_thread_for para guardar una referencia a su Thread creado, puede llamar a Thread#join en el thread para esperar a que se complete:

 x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" } a = Thread.new { print "a"; print "b"; sleep 0.2; print "c" } x.join # Let the threads finish before a.join # main thread exits... 

produce:

 abxyzc 

(Robado de la documentación de ri Thread.new . Consulte la documentación de ri Thread.join para obtener más detalles).

Por lo tanto, si modifica spawn_thread_for para guardar las referencias de Thread, puede unirse a todos ellos:

(Sin probar, pero debe dar el sabor)

 # main thread work_units = Queue.new # and fill the queue... threads = [] 10.downto(1) do threads << Thread.new do loop do w = work_units.pop Thread::exit() if w.nil? do_some_work(w) end end end # main thread continues while work threads devour work threads.each(&:join) 

En ruby ​​1.9 (y 2.0), puedes usar ThreadsWait de la stdlib para este propósito:

 require 'thread' require 'thwait' threads = [] threads << Thread.new { } threads << Thread.new { } ThreadsWait.all_waits(*threads) 

Parece que estás replicando lo que proporciona la biblioteca de Parallel Each ( Peach ).

 Thread.list.each{ |t| t.join unless t == Thread.current } 

Puedes usar Thread # join

unirse (p1 = v1) público

El hilo que llama suspenderá la ejecución y se ejecutará thr. No regresa hasta que salga el thr o hasta que hayan pasado los segundos límite. Si el límite de tiempo caduca, se devolverá nil, de lo contrario se devuelve thr.

También puede usar Enumerable # each_slice para iterar sobre las unidades de trabajo en lotes

 work_units.each_slice(10) do |batch| # handle each work unit in a thread threads = batch.map do |work_unit| spawn_thread_for work_unit end # wait until current batch work units finish before handling the next batch threads.each(&:join) end