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)
Related
I am trying to code a twitchbot.
I want to add a scheduler with for loop.
I tried that code but it‘s just printing !prima.By the way, scheduler's timer sume2 is working fine.
require "rufus-scheduler"
scheduler = Rufus::Scheduler.new
sume = ['!prime', 'sea', 'ase', '!prima']
sume2 = ['30s', '20s', '10s', '40s']
s3 = sume2.length - 1
for x in 0..s3
scheduler.interval sume2[x] do
puts sume[x]
end
end
What can I do about that?
Your problem here is you are using a for loop instead of an Enumerable method such as each.
Short answer, just change for x in 0..s3 to sume2.each_index do |x|
Longer answer, you can compare the following:
for x in 0..3
Thread.new { loop { puts(x); sleep 1 } }
end
with:
(0..3).each do |x|
Thread.new { loop { puts(x); sleep 1 } }
end
The first just prints 3 repeatedly, but the second prints 1,2,3 as intended
Why? It's because with the for loop. your x variable gets overwritten each loop. With each, your x variable is scoped to the block and you have a closure.
For more explanation, see Closures and for loops in Ruby, or https://scotch.io/tutorials/understanding-ruby-closures, or just google "closures in ruby"
Note, it's not idiomatic to use for loops in Ruby, for this particular reason
unable to understand this code how it works.
triangular_numbers = Enumerator.new do |yielder|
number = 0
count = 1
loop do
number += count
count += 1
yielder.yield number
end
end
5.times { print triangular_numbers.next, " " }
cant understand how yielder works for this block. how its yield work for number variable. how do loop runs 5 times. and how triangular_number.next works for the first time.
An enumerator is basically something that you can call next on and get something back. The yielder is the mechanism where it gives something back when next is called. Execution stops at the yield until the next call to next.
Farfetched analogy
You can think of an enumerator as a ticket machine like when you're waiting in line at a government office. When you press a button (next) it gives you a ticket. Inside the machine there's a chute where the ticket comes out. But the ticket machine is not constantly printing tickets. It waits for the button to be pressed before it prints the next ticket and puts it through the chute.
In this case the analogous code would be:
ticket_machine = Enumerator.new do |chute|
ticket = 0
loop do
#print_ticket
chute.yield ticket #waits here until you hit the button
ticket += 1
end
end
5.times { print ticket_machine.next, " " } # gets 5 tickets
Your code sample is basically the same thing, but instead of issuing tickets, it's issuing triangular numbers. The chute is the yielder where the numbers get passed through.
This is not the only way to use an enumerator, check the docs for more.
Enumerator::new accepts a block. This block, when run, receives an Enumerator::Yielder, which has a method #yield.
When the Enumerator#next is called, the block is executed, up to the first Enumerator::Yielder#yield. The execution is paused there; the value given to yield is the value that next returns. When you call next again on the same Enumerator, the execution is resumed, and proceeds until it encounters yield again.
So in your case, 5.times executes its block, intending to repeat it five times. triangular_numbers.next is called; this starts the execution of the block above. number and count are set to their values, and an infinite loop is started. number is set to 1, count is set to 2, and then we find yielder.yield. This pauses the execution of the block, and returns the control back to where next was called inside 5.times loop. next returns 1, because yielder.yield received number (1).
Second time through the 5.times loop, we want to print the next number. This stops the main execution line, and resumes the enumerator block from just after yielder.yield. The infinite loop continues; number is 3, count is 3, yielder.yield pauses the enumerator and resumes the main code. next gets 3, which gets printed.
Third, fourth and fifth time through the 5.times loop are exactly the same.
After five iterations, 5.times loop ends, and the execution proceeds past it. The enumerator is paused, ready to give the next number in sequence, if you ever call next on it again (since it has an infinite loop), but you never do, and the program exits.
I'll try to explain what it does bit by bit, so you can try to wrap your head around it.
Enumerator.new do |yielder|
end
So you instantiate an enumerator that will work over a variable called yielder.
Inside its scope you set some local vars (that will be kept as the object is reused):
number = 0
count = 1
And then you set a loop that increments number by count and count by 1 and then call yield over your argument passing number to it as an argument.
loop do
number += count
count += 1
yielder.yield number
end
5.times repeats the block passed to it 5 times. The block
-> { print triangular_numbers.next, " " }
calls print that takes n args and concatenates the parts to form a string, but does not append a newline.
The first argument is our enumerator next interaction (triangular_numbers.next), which will compute the current number and call yield on the Enumerator::Yielder that's implicitly created handling the control back to the calling Fiber along with any args that got passed to it.
(All Enumerators are implemented as "Fibers" on MRI)
So that yielder.yield call is similar to a Fiber.yield call and will allow the 5.times loop to run and return the number 1.
I'm adding a piece of code to the already clear explanation provided:
my_enum = Enumerator.new do |whatever_name_for_the_yielder|
n = 0
loop do
whatever_name_for_the_yielder.yield "Return this: #{n}"
n += 1
end
end
puts my_enum.next #=> Return this: 0
puts my_enum.next #=> Return this: 1
puts my_enum.next #=> Return this: 2
When you provide an end to the iteration, it stops with an error:
my_enum2 = Enumerator.new do |whatever_name_for_the_yielder|
2.times do |n|
whatever_name_for_the_yielder.yield "Return this: #{n}"
end
puts "Outside the loop"
end
puts my_enum2.next #=> Return this: 0
puts my_enum2.next #=> Return this: 1
puts my_enum2.next #=> Outside the loop
#=> ERROR: .....in `next': iteration reached an end (StopIteration)
Why the result is not from 1 to 10, but 10s only?
require 'thread'
def run(i)
puts i
end
while true
for i in 0..10
Thread.new{ run(i)}
end
sleep(100)
end
Result:
10
10
10
10
10
10
10
10
10
10
10
Why loop? I am running while loop, because later I want to iterate through the DB table all the time and echo any records that are retrieved from the DB.
The block that is passed to Thread.new may actually begin at some point in the future, and by that time the value of i may have changed. In your case, they all have incremented up to 10 prior to when all the threads actually run.
To fix this, use the form of Thread.new that accepts a parameter, in addition to the block:
require 'thread'
def run(i)
puts i
end
while true
for i in 0..10
Thread.new(i) { |j| run(j) }
end
sleep(100)
end
This sets the block variable j to the value of i at the time new was called.
#DavidGrayson is right.
You can see here a side effect in for loop. In your case i variable scope is whole your file. While you are expecting only a block in your for loop as a scope. Actually this is wrong approach in idiomatic Ruby. Ruby gives you iterators for this job.
(1..10).each do |i|
Thread.new{ run(i)}
end
In this case scope of variable i will be isolated in block scope what means for each iteration you will get new local (for this block) variable i.
The problem is that you have created 11 threads that are all trying to access the same variable i which was defined by the main thread of your program. One trick to avoid that is to call Thread.new inside a method; then the variable i that the thread has access to is just the particular i that was passed to the method, and it is not shared with other threads. This takes advantage of a closure.
require 'thread'
def run(i)
puts i
end
def start_thread(i)
Thread.new { run i }
end
for i in 0..10
start_thread i
sleep 0.1
end
Result:
0
1
2
3
4
5
6
7
8
9
10
(I added the sleep just to guarantee that the threads run in numerical order so we can have tidy output, but you could take it out and still have a valid program where each thread gets the correct argument.)
This little script is supposed to generate a user-specified amount of random numbers and print them. It's a multithreaded script and I think that's where my trouble lies. I'm not getting any errors, but when run the script just hangs.
num = []
while 0.upto ARGV[0].to_i do
num << rand{254}
end
current_index = 0
while current_index < num.size
chunk = num[current_index, 5]
threads = []
chunk.each do |n|
threads << Thread.new do
puts n
end
end
threads.each do |thread|
thread.join
end
current_index += chunk.size
end
You cannot use while loop with upto.
Change it to:
0.upto ARGV[0].to_i do
num << rand(254)
end
And it works properly (I've changed braces to curly one, because I believe you want 254 to be parameter here).
Sidenote:
Remember when writing threads program in Ruby, that CRuby has GIL - Global Interpreter Lock. Therefore only one thread will be operating at one time. If you want different behaviour - switch for example to jRuby. More information about GIL can be found f.e. here: http://www.jstorimer.com/blogs/workingwithcode/8085491-nobody-understands-the-gil
upto returns self, which is a number. Everything which isn't false or nil is trueish in Ruby, including numbers. So, you have a while loop whose condition is always trueish, ergo, will never stop.
Why the result is not from 1 to 10, but 10s only?
require 'thread'
def run(i)
puts i
end
while true
for i in 0..10
Thread.new{ run(i)}
end
sleep(100)
end
Result:
10
10
10
10
10
10
10
10
10
10
10
Why loop? I am running while loop, because later I want to iterate through the DB table all the time and echo any records that are retrieved from the DB.
The block that is passed to Thread.new may actually begin at some point in the future, and by that time the value of i may have changed. In your case, they all have incremented up to 10 prior to when all the threads actually run.
To fix this, use the form of Thread.new that accepts a parameter, in addition to the block:
require 'thread'
def run(i)
puts i
end
while true
for i in 0..10
Thread.new(i) { |j| run(j) }
end
sleep(100)
end
This sets the block variable j to the value of i at the time new was called.
#DavidGrayson is right.
You can see here a side effect in for loop. In your case i variable scope is whole your file. While you are expecting only a block in your for loop as a scope. Actually this is wrong approach in idiomatic Ruby. Ruby gives you iterators for this job.
(1..10).each do |i|
Thread.new{ run(i)}
end
In this case scope of variable i will be isolated in block scope what means for each iteration you will get new local (for this block) variable i.
The problem is that you have created 11 threads that are all trying to access the same variable i which was defined by the main thread of your program. One trick to avoid that is to call Thread.new inside a method; then the variable i that the thread has access to is just the particular i that was passed to the method, and it is not shared with other threads. This takes advantage of a closure.
require 'thread'
def run(i)
puts i
end
def start_thread(i)
Thread.new { run i }
end
for i in 0..10
start_thread i
sleep 0.1
end
Result:
0
1
2
3
4
5
6
7
8
9
10
(I added the sleep just to guarantee that the threads run in numerical order so we can have tidy output, but you could take it out and still have a valid program where each thread gets the correct argument.)