views:

29

answers:

2

Hi,

I am programming an application in Ruby which creates a new thread for every new job. So this is like a queue manager, where I check how many threads can be started from a database. Now when a thread finishes, I want to call the method to start a new job (i.e. a new thread). I do not want to create nested threads, so is there any way to join/terminate/exit the calling thread and pass control over to the main thread? Just to make the situation clear, there can be other threads running at this time.

I tried simply joining the calling thread, if its not the main thread and I get the following error;

"thread 0x7f8cf8dcf438 tried to join itself"

Any suggestions will be highly appreciated.

Thanks in advance.

A: 

I'd propose two solutions:

the first one is effectively to join on a thread, but join has to be called from the main thread (assuming you started all of your worker threads from the main) :

def thread_proc(s)
  sleep rand(5)
  puts "#{Thread.current.inspect}: #{s}"
end

strings = ["word", "test", "again", "value", "fox", "car"]

threads = []
2.times { 
  threads << Thread.new(strings.shift) { |s| thread_proc(s) }
}

while !threads.empty?
  threads.each { |t|
    t.join
    threads << Thread.new(strings.shift) { |s| thread_proc(s) } unless strings.empty?
    threads.delete(t)
  }
end

but that method is kind of inefficient, because creating threads over and over again induces memory and CPU overhead.

You should better synchronize a fixed pool of reused threads by using a Queue:

require 'thread'

strings = ["word", "test", "again", "value", "fox", "car"]

q = Queue.new
strings.each { |s| q << s }

threads = []
2.times { threads << Thread.new {
  while !q.empty?
    s = q.pop
    sleep(rand(5))
    puts "#{Thread.current.inspect}: #{s}"
  end
}}

threads.each { |t| t.join }
SirDarius
A: 
t1 = Thread.new { Thread.current[:status] = "1"; sleep 10; Thread.pass; sleep 100 }  
t2 = Thread.new { Thread.current[:status] = "2"; sleep 1000 }  
t3 = Thread.new { Thread.current[:status] = "3"; sleep 1000 }  

puts Thread.list.map {|X| x[:status] }
#=> 1,2,3

Thread.list.each do |x| 
  if x[:status] == 2 
    x.kill # kill the thread
    break
  end
end  

puts Thread.list.map {|X| x[:status] }
#=> 1,3

"Thread::pass" will pass control to the scheduler which can now schedule any other thread. The thread has voluntarily given up control to the scheduler - we cannot specify to pass control onto a specific thread

"Thread#kill" will kill the instance the thread

"Thread::list" will return the list of threads

Threads are managed by the scheduler, if you want explicit control then checkout fibers. But it has some gotchas, fibers are not supported in JRuby.

also checkout thread local variables, it will help you to communicate the status or return value of the thread, without joining to the thread.

http://github.com/defunkt/resque is a good option for a queue, check it out. Also try JRuby if you are going make heavy use of threads. It' advantage is that it will wrap java threads in ruby goodness.

deepak