How work these threads? - ruby

I have a small code just for test
#!/usr/bin/env ruby
def A
puts "A"
sleep 2
end
def B
puts "B"
sleep 2
end
[
Thread.new(loop{A()}),
Thread.new(loop{B()})
].each do |thr|
thr.join
end
and it didn't works as I wish.
I hoped that I'll get
A
B
A
B
and so on, but I got just
A
A
A
A
It means that only 1st thread was started.
Does it means that ruby waits when 1st Thread will be closed to start the 2nd one ?
How I could run Thread as thread, I'd like to have threads in my app which will be in parallel make their work and main application thread will be its own job.
What could you advise me ?

Instead of running the loop in the threads, the code is running the loop inside the main thread; which make the threads not to start because of the infinite loop.
Replace following lines (parentheses):
[
Thread.new(loop{A()}),
Thread.new(loop{B()})
]
with (braces):
[
Thread.new{loop{A()}},
Thread.new{loop{B()}}
]
to pass the block instead of the return value of the (infinite) loop.

Your call to Thread ctor does not do what you expect. You are passing the result of the loop block to the Thread constructor. This way, the loop has to end before the Thread could start. But since your loop is never ending, you only see the A() method output which is being executed in the current thread.
Try calling it this way:
[
Thread.new{loop{A()}},
Thread.new{loop{B()}}
]

Related

How to set a loop to run continously without getting stuck in infinite loop?

I have some code the executes every n minutes and updates some values. I check those values with conditionals and if needed, I call a loop that makes an LED blink. That loop function looks like:
def blink_led_constant
while 1
# RPi::GPIO.set_high 18
# sleep(1)
# RPi::GPIO.set_low 18
# sleep(1)
end
end
Basically I want this LED to blink in the background until a certain condition is met. How do I set this to function to run, but also continue past the function call to reiterate my program?
Right now based on how I wrote this, if this function is called it gets stuck infinitely.
Thanks
You can use a thread to do this.
def blink_led_constant
loop do
# RPi::GPIO.set_high 18
# sleep(1)
# RPi::GPIO.set_low 18
# sleep(1)
end
end
thread = Thread.new { blink_led_constant }
Then you can execute other commands while blink_led_constant runs in the background. Change the loop do to a while if you want it to break once a condition is met. Once the condition is met to stop the led from blinking call Thread.kill(thread) or thread.kill.

Ruby threads calling the same function with different arguments

I am calling the same Ruby function with a number of threads (for example 10 threads). Each thread passes different argument to function.
Example:
def test thread_no
puts "In thread no." + thread_no.to_s
end
num_threads = 6
threads=[]
for thread_no in 1..num_threads
puts "Creating thread no. "+thread_no.to_s
threads << Thread.new{test(thread_no)}
end
threads.each { |thr| thr.join }
Output:
Creating thread no. 1
Creating thread no. 2
Creating thread no. 3
Creating thread no. 4
In thread no.4
Creating thread no. 5
Creating thread no. 6
In thread no.6
In thread no.6
In thread no.6
In thread no.6
In thread no.6
Of course I want to get output: In thread no. 1 (2,3,4,5,6) Can I somehow achieve that this would work?
The problem is the for-loop. In Ruby, it reuses a single variable.
So all blocks of the thread bodies access the same variable. An this variable is 6 at the end of the loop. The thread itself may start only after the loop has ended.
You can solve this by using the each-loops. They are more cleanly implemented, each loop variable exists on its own.
(1..num_threads).each do | thread_no |
puts "Creating thread no. "+thread_no.to_s
threads << Thread.new{test(thread_no)}
end
Unfortunately, for loops in ruby are a source of surprises. So it is best to always use each loops.
Addition:
You an also give Thread.new one or several parameters, and these parameters get passed into the thread body block. This way you can make sure that the block uses no vars outside it's own scope, so it also works with for-loops.
threads << Thread.new(thread_no){|n| test(n) }
#Meier already mentioned the reason why for-end spits out different result than expected.
for loop is language syntax construction, it reuses the same local variable thread_no and thread_no yields 6 because your for loop ends before the last few threads start executing.
In order to get rid of such issue, you can keep a copy of the exact thread_no in an another scope - such as -
def test thread_no
puts "In thread no." + thread_no.to_s
end
num_threads = 6
threads = []
for thread_no in 1..num_threads
threads << -> (thread_no) { Thread.new { test(thread_no) } }. (thread_no)
end
threads.each { |thr| thr.join }

Ruby Variable Reference Issue

I am not fluent in ruby and am having trouble with the following code example. I want to pass the array index to the thread function. When I run this code, all threads print "4". They should instead print "0 1 2 3 4" (in any order).
It seems that the num variable is being shared between all iterations of the loop and passes a reference to the "test" function. The loop finishes before the threads start and num is left equal to 4.
What is going on and how do I get the correct behavior?
NUM_THREADS = 5
def test(num)
puts num.to_s()
end
threads = Array.new(NUM_THREADS)
for i in 0..(NUM_THREADS - 1)
num = i
threads[i] = Thread.new{test(num)}
end
for i in 0..(NUM_THREADS - 1)
threads[i].join
end
Your script does what I would expect in Unix but not in Windows, most likely because the thread instantiation is competing with the for loop for using the num value. I think the reason is that the for loop does not create a closure, so after finishing that loop num is equal to 4:
for i in 0..4
end
puts i
# => 4
To fix it (and write more idiomatic Ruby), you could write something like this:
NUM_THREADS = 5
def test(num)
puts num # to_s is unnecessary
end
# Create an array for each thread that runs test on each index
threads = NUM_THREADS.times.map { |i| Thread.new { test i } }
# Call the join method on each thread
threads.each(&:join)
where i would be local to the map block.
"What is going on?" => The scope of num is the main environment, so it is shared by all threads (The only thing surrounding it is the for keyword, which does not create a scope). The execution of puts in all threads was later than the for loop on i incrementing it to 4. A variable passed to a thread as an argument (such as num below) becomes a block argument, and will not be shared outside of the thread.
NUM_THREADS = 5
threads = Array.new(NUM_THREADS){|i| Thread.new(i){|num| puts num}}.each(&:join)

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