views:

83

answers:

2

Calling Thread.join blocks the current (main) thread. However not calling join results in all spawned threads to be killed when the main thread exits. How does one spawn persistent children threads in Ruby without blocking the main thread?

Here's a typical use of join.

for i in 1..100 do
  puts "Creating thread #{i}"
  t = Thread.new(i) do |j|
    sleep 1
    puts "Thread #{j} done"
  end
  t.join
end
puts "#{Thread.list.size} threads"

This gives

     Creating thread 1  
     Thread 1 done  
     Creating thread 2  
     Thread 2 done  
     ...  
     1 threads  

but I'm looking for how to get this

    Creating thread 1  
    Creating thread 2  
    ...  
    101 threads  
    Thread 1 done  
    Thread 2 done  
    ...  

The code gives the same output in both Ruby 1.8.7 and 1.9.2

+2  A: 

You need to join the threads outside of the loop.

for i in 1..100 do
  puts "Creating thread #{i}"
  t = Thread.new(i) do |mi|
    sleep 1
    puts "Thread #{mi} done"
  end
end

# Wait for all threads to end
Thread.list.each do |t|
  # Wait for the thread to finish if it isn't this thread (i.e. the main thread).
  t.join if t != Thread.current
 end
joast
+6  A: 

You simply accumulate the threads in another container, then join them one-by-one after they've all been created:

my_threads = []
for i in 1..100 do
    puts "Creating thread #{i}"
    my_threads << Thread.new(i) do |j|
        sleep 1
        puts "Thread #{j} done"
    end
end
puts "#{Thread.list.size} threads"

my_threads.each do |t|
    t.join
end

You also can't bind the thread to the i variable because i gets constantly overwritten, and your output will be 100 lines of "Thread 100 done"; instead, you have to bind it to a copy of i, which I have cleverly named j.

Mark Rushakoff
Yes, that works.I would have thought that Ruby would had a way to handle this implicitly instead of having to keep track of the threads and performing a join call at the end.And yes, I shouldn't use the global i within each thread. Basic stuff. Thanks for the clean fix. I'll update the code.
Alkaline
In Ruby 1.9, block parameters are always local, so you wouldn't need to rename the variable. You'd get a warning, though. (*Variable `i` shadows outer variable* or something like that.)
Jörg W Mittag