1

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
2
  • Define "correct behavior". Commented Nov 19, 2013 at 7:54
  • Output 0 1 2 3 4, but not necessarily in that order. Commented Nov 19, 2013 at 8:04

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

"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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.