Wait for a thread to die in Ruby - ruby

It appears that, in Ruby 2.4 and 2.5, threads don't die as soon as you invoke #kill on them. This code snippet will print Not dead a few times:
thread = Thread.new { loop {} }
thread.kill
puts "Not dead" while thread.alive?
I'd like to block execution of the main thread until the secondary thread is killed. I tried using thread.join.kill, but of course this blocks the main thread because the thread's loop never terminates.
How can I ensure that a thread is killed before the main thread continues?

Figured it out; you can still #join the thread after killing it, so you can use thread.kill.join to block until the thread dies.
This code never prints Not dead:
thread = Thread.new { loop {} }
thread.kill.join
puts "Not dead" while thread.alive?

I'm doing this:
thread = Thread.new { loop {} }
thread.kill
sleep 0.001 while thread.alive?
This is how I terminate threads in my ThreadPool.

Related

Why does the presence of a forked process in a thread change the behavior of sleep within a trap block?

This works as expected:
signals = %w[INT TERM]
signals.each do |signal|
Signal.trap(signal) do
puts "trapping the signal and sleeping for 5 seconds"
sleep 5
puts "done, exiting"
exit
end
end
sleep 100
so does this:
# ...signal trap code from above...
t = Thread.new{ sleep 10 }
t.join
so does this:
# ...signal trap code from above...
`sleep 10`
however, this does not:
# ...signal trap code from above...
t = Thread.new{ `sleep 10` }
t.join
For the first three, starting the code and then immediately sending control-c results in ruby waiting 5 seconds before exiting.
For the fourth, starting the code and then immediately sending control-c results in ruby immediately exiting. What's amazing is that the two puts messages, "trapping the..." and "done...", are both printed, but the sleep 5 in between them is seemingly skipped over.
using terrapin instead of backticks produces another clue - terrapin complains that the child processes exited with non-zero state. when attempting to print this status, it prints nothing, perhaps suggesting that the process was unceremoniously hard killed.
So it seems that ruby has some sort of default behavior regarding child processes created within threads that are not the main thread. I suspected this might be by design but I wasn't able to find any documentation or discussion about it.
I also tried
t = Thread.new{ `ls -R /` }
instead of sleep, just in case there was some sort of interaction with competing sleep implementations, same behavior.
Why is this happening?
More Experiments
This also behaves as expected. it waits 10 seconds for the subprocess within the thread to finish. so, the strange behavior only happens in the context of a signal trap.
thread = Thread.new { `sleep 10` }
thread.join
To test if something is happening involving a subthread trapping the signal at the same time, either by design or from a bug. But this behaves as expected:
#main_thread = Thread.current.object_id
puts "main thread: #{#main_thread}"
signals = %w[INT TERM]
signals.each do |signal|
Signal.trap(signal) do
puts Thread.current.object_id
next unless Thread.current.object_id == #main_thread
puts "thread from trap: #{Thread.current.object_id}"
puts "trapping the signal and sleeping for 5 seconds"
sleep 5
puts "done, exiting"
exit
end
end

How to start a new thread every x seconds

I want to start a new thread every x seconds in Ruby, but wasn´t able to figure it out.
Usually the thread execution takes longer then the x seconds, all I managed was something that starts a new thread after the previous one finished.
So I want to start a new thread after x seconds, now matter how many previous threads are still running.
Any ideas?
threads = [] # array of Thread in case you need to do something with all the Threads
# like threads.each { |t| t.join }
1.upto(5) do |n|
threads << Thread.new { puts "Thread #{n}!" }
sleep 1 # or more seconds if need it
end

Multi Threading in Ruby

I need to create 3 threads.
Each thread will print on the screen a collor and sleep for x seconds.
Thread A will print red; Thread B will print yellow; Thread C will print green;
All threads must wait until its their turn to print.
The first thread to print must be Red, after printing, Red will tell Yellow that's its turn to print and so on.
The threads must be able to print multiple times (user specific)
I'm stuck because calling #firstFlag.signal outside a Thread isn't working and the 3 threads aren't working on the right order
How do I make the red Thread go first?
my code so far:
#lock = Mutex.new
#firstFlag = ConditionVariable.new
#secondFlag = ConditionVariable.new
#thirdFlag = ConditionVariable.new
print "Tell me n's vallue:"
#n = gets.to_i
#threads = Array.new
#threads << Thread.new() {
t = Random.rand(1..3)
n = 0
#lock.synchronize {
for i in 0...#n do
#firstFlag.wait(#lock, t)
puts "red : #{t}s"
sleep(t)
#secondFlag.signal
end
}
}
#threads << Thread.new() {
t = Random.rand(1..3)
n = 0
#lock.synchronize {
for i in 0...#n do
#secondFlag.wait(#lock, t)
puts "yellow : #{t}s"
sleep(t)
#thirdFlag.signal
end
}
}
#threads << Thread.new() {
t = Random.rand(1..3)
n = 0
#lock.synchronize {
for i in 0...#n do
#thirdFlag.wait(#lock, t)
puts "green : #{t}s"
sleep(t)
#firstFlag.signal
end
}
}
#threads.each {|t| t.join}
#firstFlag.signal
There are three bugs in your code:
First bug
Your wait calls use a timeout. This means your threads will become de-synchronized from your intended sequence, because the timeout will let each thread slip past your intended wait point.
Solution: change all your wait calls to NOT use a timeout:
#xxxxFlag.wait(#lock)
Second bug
You put your sequence trigger AFTER your Thread.join call in the end. Your join call will never return, and hence the last statement in your code will never be executed, and your thread sequence will never start.
Solution: change the order to signal the sequence start first, and then join the threads:
#firstFlag.signal
#threads.each {|t| t.join}
Third bug
The problem with a wait/signal construction is that it does not buffer the signals.
Therefore you have to ensure all threads are in their wait state before calling signal, otherwise you may encounter a race condition where a thread calls signal before another thread has called wait.
Solution: This a bit harder to solve, although it is possible to solve with Queue. But I propose a complete rethinking of your code instead. See below for the full solution.
Better solution
I think you need to rethink the whole construction, and instead of condition variables just use Queue for everything. Now the code becomes much less brittle, and because Queue itself is thread safe, you do not need any critical sections any more.
The advantage of Queue is that you can use it like a wait/signal construction, but it buffers the signals, which makes everything much simpler in this case.
Now we can rewrite the code:
redq = Queue.new
yellowq = Queue.new
greenq = Queue.new
Then each thread becomes like this:
#threads << Thread.new() {
t = Random.rand(1..3)
n = 0
for i in 0...#n do
redq.pop
puts "red : #{t}s"
sleep(t)
yellowq.push(1)
end
}
And finally to kick off the whole sequence:
redq.push(1)
#threads.each { |t| t.join }
I'd redesign this slightly. Think of your ConditionVariables as flags that a thread uses to say it's done for now, and name them accordingly:
#lock = Mutex.new
#thread_a_done = ConditionVariable.new
#thread_b_done = ConditionVariable.new
#thread_c_done = ConditionVariable.new
Now, thread A signals it's done by doing #thread_a_done.signal, and thread B can wait for that signal, etc. Thread A of course needs to wait until thread C is done, so we get this kind of structure:
#threads << Thread.new() {
t = Random.rand(1..3)
#lock.synchronize {
for i in 0...#n do
#thread_c_done.wait(#lock)
puts "A: red : #{t}s"
sleep(t)
#thread_a_done.signal
end
}
}
A problem here is that you need to make sure that thread A in the first iteration doesn't wait for a flag signal. After all, it's to go first, so it shouldn't wait for anyone else. So modify it to:
#thread_c_done.wait(#lock) unless i == 0
Finally, once you have created your threads, kick them all off by invoking run, then join on each thread (so that your program doesn't exit before the last thread is done):
#threads.each(&:run)
#threads.each(&:join)
Oh btw I'd get rid of the timeouts in your wait as well. You have a hard requirement that they go in order. If you make the signal wait time out you screw that up - threads might still "jump the queue" so to speak.
EDIT as #casper remarked below, this still has a potential race condition: Thread A could call signal before thread B is waiting to receive it, in which case thread B will miss it and just wait indefinitely. A possible way to fix this is to use some form of a CountDownLatch - a shared object that all threads can wait on, which gets released as soon as all threads have signalled that they're ready. The ruby-concurrency gem has an implementation of this, and in fact might have other interesting things to use for more elegant multi-threaded programming.
Sticking with pure ruby though, you could possibly fix this by adding a second Mutex that guards shared access to a boolean flag to indicate the thread is ready.
Ok, thank you guys that answered. I've found a solution:
I've created a fourth thread. Because I found out that calling "#firstFlag.signal" outside a thread doesn't work, because ruby has a "main thread" that sleeps when you "run" other threads.
So, "#firstFlag.signal" calling must be inside a thread so it can be on the same level of the CV.wait
I solved the issue using this:
#threads << Thread.new {
sleep 1
#firstFlag.signal
}
This fourth thread will wait for 1 sec before sending the first signal to red. This only sec seems to be enough for the others thread reach the wait point.
And, I've removed the timeout, as you sugested.
//Edit//
I realized I don't need a fourth Thread, I could just make thread C do the first signal.
I made thread C sleep for 1 sec to wait the other two threads enter in wait state, then it signals red to start and goes to wait too
#threads << Thread.new() {
sleep 1
#redFlag.signal
t = Random.rand(1..3)
n = 0
#lock.synchronize {
for i in 0...#n do
#greenFlag.wait(#lock)
puts "verde : #{t}s"
sleep(t)
#redFlag.signal
n += 1
end
}
}

Thread blocks main Thread in Ruby 1.9

I have this code:
...
....
ping_thread = Thread.new {
loop do
sleep 2
ping
end
}
ping_thread.join
puts "TEST"
...
....
it executes the ping function but does not move on printing the "TEST" statement. The ping function has a simple "puts "PING"" statement inside of it. I want the ping_thread to run as a background thread.
Thread.new will start your thread running in the background automatically, and Thread#join will block until that thread finishes its job. So normally, joining the thread is the last thing you do, when the main execution thread is done doing its work.
Try the following code and see if it does what you want:
ping_thread = Thread.new {
loop do
sleep 2
puts "ping"
end
}
puts "TEST"
ping_thread.join
Note that if you don't join the thead in the end, it will die when the main execution thread completes its work, so that join is necessary.
.join means just wait here until thread is dead

How do I manage ruby threads so they finish all their work?

I have a computation that can be divided into independent units and the way I'm dealing with it now is by creating a fixed number of threads and then handing off chunks of work to be done in each thread. So in pseudo code here's what it looks like
# 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
Basically once the initial number of threads is created each one does some work and then keeps taking stuff to be done from the work stack until nothing is left. Everything works fine when I run things in irb but when I execute the script using the interpreter things don't work out so well. I'm not sure how to make the main thread wait until all the work is finished. Is there a nice way of doing this or am I stuck with executing sleep 10 until work_units.empty? in the main thread
In ruby 1.9 (and 2.0), you can use ThreadsWait from the stdlib for this purpose:
require 'thread'
require 'thwait'
threads = []
threads << Thread.new { }
threads << Thread.new { }
ThreadsWait.all_waits(*threads)
If you modify spawn_thread_for to save a reference to your created Thread, then you can call Thread#join on the thread to wait for completion:
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...
produces:
abxyzc
(Stolen from the ri Thread.new documentation. See the ri Thread.join documentation for some more details.)
So, if you amend spawn_thread_for to save the Thread references, you can join on them all:
(Untested, but ought to give the flavor)
# 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)
Thread.list.each{ |t| t.join unless t == Thread.current }
It seems like you are replicating what the Parallel Each (Peach) library provides.
You can use Thread#join
join(p1 = v1) public
The calling thread will suspend execution and run thr. Does not return until thr exits or until limit seconds have passed. If the time limit expires, nil will be returned, otherwise thr is returned.
Also you can use Enumerable#each_slice to iterate over the work units in batches
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

Resources