What's the correct Ruby pattern for waiting for a background thread to start? - ruby

I need to write a Ruby program that executes a background program, and performs some functionality on it.
Before doing any operation, the main thread needs to make sure that the background thread is started. What the correct pattern?
This is not exact:
condition = ConditionVariable.new
mutex = Mutex.new
thread = Thread.new do
mutex.synchronize { condition.signal }
# background work
end
mutex.synchronize { condition.wait(mutex) }
# other work
because :signal could execute before :wait, blocking the main thread.
An exact solution is:
thread = Thread.new do
Thread.current[:started] = true
# background work
end
sleep 0.01 while thread[:started].nil?
# other work
however, it uses sleep, which I'd like to avoid.
Another exact, but more complex, solution is:
mutex = Mutex.new
condition = ConditionVariable.new
thread = Thread.new do
mutex.synchronize do
Thread.current[:started] = true
condition.signal
end
# background work
end
mutex.synchronize do
condition.wait(mutex) if !thread[:started]
end
# other work
Is there any exact, simple and idiomatic way to structure this functionality?

You could use a Queue:
queue = Queue.new
thread = Thread.new do
queue.push :ready
# background work
end
queue.pop
# other work
Queue#pop will wait until an item is available and return it.

Related

How to gracefully shutdown a thread in Ruby

I have been experimenting multi-threading concept in Ruby for the past a week.
For practising, I am designing a file downloader that makes parallel requests for a collection of URLs. Currently I need to safely shutdown threads when interrupt signal is triggered. I have read the theory of multi-threading and catching a signal at runtime. Yet despite the whole those theoretical knowledge, I still don't have any idea about how to use them in practice.
I am leaving my proof of concept work below, anyhow.
class MultiThread
attr_reader :limit, :threads, :queue
def initialize(limit)
#limit = limit
#threads = []
#queue = Queue.new
end
def add(*args, &block)
queue << [block, args]
end
def invoke
1.upto(limit).each { threads << spawn_thread }
threads.each(&:join)
end
private
def spawn_thread
Thread.new do
Thread.handle_interrupt(RuntimeError => :on_blocking) do
# Nothing to do
end
until queue.empty?
block, args = queue.pop
block&.call(*args)
end
end
end
end
urls = %w[https://example.com]
thread = MultiThread.new(2)
urls.each do |url|
thread.add do
puts "Downloading #{url}..."
sleep 1
end
end
thread.invoke
Yeah, the docs for handle_interrupt are confusing. Try this, which I based on the connection_pool gem used by e.g. puma.
$stdout.sync = true
threads = 3.times.map { |i|
Thread.new {
Thread.handle_interrupt(Exception => :never) do
begin
Thread.handle_interrupt(Exception => :immediate) do
puts "Thread #{i} doing work"
sleep 1000
end
ensure
puts "Thread #{i} cleaning up"
end
end
}
}
Signal.trap("INT") {
puts 'Exiting gracefully'
threads.each { |t|
puts 'killing thread'
t.kill
}
exit
}
threads.each { |t| t.join }
Output:
Thread 1 doing work
Thread 2 doing work
Thread 0 doing work
^CExiting gracefully
killing thread
killing thread
killing thread
Thread 0 cleaning up
Thread 1 cleaning up
Thread 2 cleaning up

How do you keep a ruby thread alive

Running this
# in initialize
#queue = Queue.new
#threads = Array.new(NUM_THREADS) do
Thread.new do
until #queue.empty?
puts #queue.shift
end
end
end
# later in another method, calling
#threads.each { |t| puts t.alive? } # puts false
#queue.push('something else')
# new item is not processed by thread
How do I keep a Ruby thread alive so it can keep accepting stuff from a queue?
The issue is you initialize the threads before adding anything to the queue. The threads start and die before the #queue.push line is run.
If you want to keep alive the thread even if there is nothing in the queue, you can change the logic of the thread so it loops forever:
Thread.new do
loop do
if val = #queue.shift
puts val
end
end
end
You could decrease the CPU consumption by putting a sleep call inside the thread's loop, say it sleeps 0.1 seconds each iteration and thus it could process a max of 10 items per second. For example running the following in my Ruby REPL raises the process' CPU consumption from around 0 to 25% (which is undesirably high)
100.times { Thread.new { loop { } } }
But the following uses less than 1%:
100.times { Thread.new { loop { sleep 0.1 } } }
There are other ways to manage CPU consumption of background processes other than putting arbitrary sleep numbers in there: for example eventmachine, resque, or sidekiq.
You probably want to use Queue from Ruby core library.
Methods pop, deq and shift can be used to retrieve data from the queue.
With these methods, according to the documentation
If the queue is empty, the calling thread is suspended until data is
pushed onto the queue.
With Queue, your code snippet would look like so
#queue = Queue.new
#threads = Array.new(NUM_THREADS) do
Thread.new do
while (item = #queue.shift)
puts item
end
end
end
# later in another method
#threads.each { |t| puts t.alive? } # true
#queue.push('something else') # is printed out
Queue#shift keeps the thread waiting, until something is pushed into the queue. You still need the loop, so that after handling the item, the thread will stay alive, suspended, waiting for the next item.

Ruby synchronisation: How to make threads work one after another in proper order?

My problem is that I don't know how synchronise multiple threads using Ruby. The task is to create six threads and start them immediately. All of them should do some work (for example puts "Thread 1" Hi") one after another in the order I need it to work.
I've tried to work with Mutex, Monitor and Condition Variable, but all of them worked in random order. Could anybody explain how to achieve my goal?
After some time of struggling with Mutex and Condition Variable I've achieved my goal.
This code is a little bit messy, and I intentionally did't use cycles for "clearer view".
cv = ConditionVariable.new
mutex = Mutex.new
mutex2 = Mutex.new
cv2 = ConditionVariable.new
mutex3 = Mutex.new
cv3 = ConditionVariable.new
mutex4 = Mutex.new
cv4 = ConditionVariable.new
mutex5 = Mutex.new
cv5 = ConditionVariable.new
mutex6 = Mutex.new
cv6 = ConditionVariable.new
Thread.new do
mutex.synchronize {
puts 'First: Hi'
cv.wait(mutex)
puts 'First: Bye'
#cv.wait(mutex)
cv.signal
puts 'First: One more time'
}
end
Thread.new do
mutex.synchronize {
puts 'Second: Hi'
cv.signal
cv.wait(mutex)
puts 'Second:Bye'
cv.signal
}
mutex2.synchronize {
puts 'Second: Starting third'
cv2.signal
}
end
Thread.new do
mutex2.synchronize {
cv2.wait(mutex2)
puts 'Third: Hi'
}
mutex3.synchronize {
puts 'Third: Starting forth'
cv3.signal
}
end
Thread.new do
mutex3.synchronize {
cv3.wait(mutex3)
puts 'Forth: Hi'
}
mutex4.synchronize {
puts 'Forth: Starting fifth'
cv4.signal
}
end
Thread.new do
mutex4.synchronize {
cv4.wait(mutex4)
puts 'Fifth: Hi'
}
mutex5.synchronize {
puts 'Fifth: Starting sixth'
cv5.signal
}
end
Thread.new {
mutex5.synchronize {
cv5.wait(mutex5)
puts 'Sixth:Hi'
}
}
sleep 2
Using Queue as a PV Semaphore
You can abuse Queue, using it like a traditional PV Semaphore. To do this, you create an instance of Queue:
require 'thread'
...
sem = Queue.new
When a thread needs to wait, it calls Queue#deq:
# waiting thread
sem.deq
When some other thread wants to unblock the waiting thread, it pushes something (anything) onto the queue:
# another thread that wants to unblock the waiting thread
sem.enq :go
A Worker class
Here's a worker class that uses Queue to synchronize its start and stop:
class Worker
def initialize(worker_number)
#start = Queue.new
Thread.new do
#start.deq
puts "Thread #{worker_number}"
#when_done.call
end
end
def start
#start.enq :start
end
def when_done(&block)
#when_done = block
end
end
When constructed, a worker creates a thread, but that thread then waits on the #start queue. Not until #start is called will the thread unblock.
When done, the thread will execute the block that was called to #when_done. We'll see how this is used in just a moment.
Creating workers
First, let's make sure that if any threads raise an exception, we get to find out about it:
Thread.abort_on_exception = true
We'll need six workers:
workers = (1..6).map { |i| Worker.new(i) }
Telling each worker what to do when it's done
Here's where #when_done comes into play:
workers.each_cons(2) do |w1, w2|
w1.when_done { w2.start }
end
This takes each pair of workers in turn. Each worker except the last is told, that when it finishes, it should start the worker after it. That just leaves the last worker. When it finishes, we want it to notify this thread:
all_done = Queue.new
workers.last.when_done { all_done.enq :done }
Let's Go!
Now all that remains is to start the first thread:
workers.first.start
and wait for the last thread to finish:
all_done.deq
The output:
Thread 1
Thread 2
Thread 3
Thread 4
Thread 5
Thread 6
If you're just getting started with threads, you might want to try something simple. Let the 1st thread sleep for 1 second, the 2nd for 2 seconds, the 3rd for 3 seconds and so on:
$stdout.sync = true
threads = []
(1..6).each do |i|
threads << Thread.new {
sleep i
puts "Hi from thread #{i}"
}
end
threads.each(&:join)
Output (takes 6 seconds because the threads run in parallel):
Hi from thread 1
Hi from thread 2
Hi from thread 3
Hi from thread 4
Hi from thread 5
Hi from thread 6
You can assign each a number, which will denote its place in the queue, and check it to see whose turn it is:
class QueuedWorker
def initialize(mutex, condition_variable, my_turn)
#mutex = mutex
#my_turn = my_turn
#condition_variable = condition_variable
end
def self.turn
#turn ||= 0
end
def self.done
#turn = turn + 1
end
def run
loop do
#mutex.synchronize do
if QueuedWorker.turn == #my_turn
# do actual work
QueuedWorker.done
#condition_variable.signal
return
end
#condition_variable.signal
#condition_variable.wait(#mutex)
end
end
end
end
mutex = Mutex.new
cv = ConditionVariable.new
(0..10).each do |i|
Thread.new do
QueueWorker.new(mutex, cv, i).run
end
end
That being said, the implementation is awkward, since threading are specifically not built for serial work. If you need something to work serially, do it in a single thread.

Stop Ruby - jRuby - thread after a certain time

I'm trying to create a simple multithreaded program with jRuby. It needs to start and stop threads based on a specified amount of time e.g. run for five seconds then stop. I'm pretty new to this sort of stuff, so it's probably pretty basic but I can't get it to work.
The relevant code looks like this:
require 'java'
require 'timeout'
require './lib/t1.rb'
require './lib/t2.rb'
class Threads
[...]
def manage_threads
thread2 = T2.new
# Wait for 5 seconds before the thread starts running..
thread2.run(wait_time = 5)
Timeout::timeout(10) do
thread1 = T1.new {}
end
end
class T1 < Thread
def initialize
while super.status != "sleep"
puts "Thread 1"
sleep(1)
end
end
end
class T2
include java.lang.Runnable
def run wait_time
thread = Thread.new do
sleep(wait_time)
loop do
puts "Thread 2"
sleep(1)
end
end
end
def stop_thread(after_run_time)
sleep(after_run_time)
end
end
I have already tried a couple if things, for example:
# Used timeout
Timeout::timeout(10) do
thread1 = T1.new {}
end
# This kinda works, except that it terminates the program and therefore isn't the behavior
# I want.
Does anyone have a suggestion on how to 1. start a thread, run it for a while. 2. Start a new thread, run both thread in parallel. 2. Stop thread 1 but keep running thread 2. Any tips/suggestions would be appreciated.
I think I solved it.
This did the trick:
def run wait_time
thread = Thread.new do
sleep(wait_time)
second_counter = 0
loop do
puts "Thread 2"
second_counter += 1
if second_counter == 15
sleep
end
sleep(1)
end
end
end

conditional variable in main thread

In my main thread, I am trying to wait for two resources from two separate threads. The way I implemented is as below:
require 'thread'
def new_thread
Thread.current[:ready_to_go] = false
puts "in thread: new thread"
sleep(5)
puts "in thread: sleep finished"
Thread.current[:ready_to_go] = true
sleep(2)
puts "in thread: back to thread again!"
end
thread1 = Thread.new do
new_thread
end
thread2 = Thread.new do
new_thread
end
# the main thread wait for ready_to_go to start
while (!(thread1[:ready_to_go] && thread2[:ready_to_go]))
sleep(0.5)
end
puts "back to main!"
sleep(8)
puts "main sleep over!"
thread1.join
thread2.join
Is there any better way to implement this? I tried to use conditional variables: the two threads signal the conditional variables and the main thread waits for them. But the wait method requires a mutex in my main thread, which I am trying to avoid.
I'm not familiar with Ruby, but the first Google result for "Ruby wait for thread" says:
you can wait for a particular thread to finish by calling that thread's Thread#join method. The calling thread will block until the given thread is finished. By calling join on each of the requestor threads, you can make sure that all three requests have completed before you terminate the main program.
It's generally best to use synchronization methods to wait for something to complete, rather than looping until a particular state is reached.
One easy way would be to get a Queue (let's call it myqueue). It's threadsafe and located in the Thread module
Instead of
Thread.current[:ready_to_go] = true
... do
myqueue.push :ready_to_go
And then your main thread would be:
junk = myqueue.pop # wait for thread one to push
junk = myqueue.pop # wait for thread two to push
# go on with your work

Resources