web-dev-qa-db-fra.com

Comment gérer les threads Ruby pour qu'ils terminent tout leur travail?

J'ai un calcul qui peut être divisé en unités indépendantes et la façon dont je le traite est de créer un nombre fixe de threads, puis de transférer des morceaux de travail à effectuer dans chaque thread. Donc, dans le pseudo-code, voici à quoi ça ressemble

# 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

En gros, une fois que le nombre initial de threads est créé, chacun effectue un travail puis continue à prendre des tâches à exécuter de la pile de travail jusqu'à ce qu'il ne reste plus rien. Tout fonctionne bien lorsque j'exécute des tâches dans irb, mais lorsque j'exécute le script à l'aide d'un interprète, les choses ne fonctionnent pas aussi bien. Je ne sais pas comment faire en sorte que le fil principal attende que tout le travail soit terminé. Y at-il une bonne façon de faire cela ou suis-je coincé avec l'exécution de sleep 10 until work_units.empty? dans le thread principal

23
davidk01

Si vous modifiez spawn_thread_for pour enregistrer une référence à votre Thread créée, vous pouvez appeler Thread#join sur le thread pour attendre son achèvement:

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...

produit:

abxyzc

(Volé dans la documentation ri Thread.new. Voir la documentation ri Thread.join pour plus de détails.)

Ainsi, si vous modifiez spawn_thread_for pour enregistrer les références de thread, vous pouvez les rejoindre toutes:

(Non testé, mais devrait donner la saveur)

# 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)
14
sarnold

Dans Ruby 1.9 (et 2.0), vous pouvez utiliser ThreadsWait à partir de stdlib à cette fin:

require 'thread'
require 'thwait'

threads = []
threads << Thread.new { }
threads << Thread.new { }
ThreadsWait.all_waits(*threads)
32
esad

On dirait que vous reproduisez ce que la bibliothèque Parallel Each ( Peach ) fournit.

3
Hector Castro
Thread.list.each{ |t| t.join unless t == Thread.current }
1
brauliobo

Vous pouvez utiliser Thread # join

rejoindre (p1 = v1) public

Le thread appelant suspendra l'exécution et exécutera thr. Ne revient pas avant que thr ne quitte ou jusqu'à ce que le nombre de secondes écoulé soit écoulé. Si le délai expire, nil sera renvoyé, sinon thr est renvoyé.

Vous pouvez aussi utiliser Enumerable # each_slice pour parcourir les unités de travail par lots.

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
0
Hirurg103