Hello,
count = 0
threads = []
10.times do |i|
threads[i] = Thread.new do
sleep(rand(0.1))
Thread.current["mycount"] = count
count += 1
end
end
threads.each {|t| t.join; print t["mycount"], ", " }
For the code above, why the output numbers are random, rather than
from 0 to 9 by increasing?
Thanks.
on 2010-03-09 05:40
on 2010-03-09 09:12
2010/3/9 Jean G. <rubynewbee@gmail.com>: > end > end > > threads.each {|t| t.join; print t["mycount"], ", " } > > > For the code above, why the output numbers are random, rather than > from 0 to 9 by increasing? Because there are no guarantees about thread scheduling. Btw, your code is not really thread safe since you access a shared resource without proper synchronization (although it might work on some Ruby platforms). Kind regards robert
on 2010-03-09 17:43
Jean G. wrote: > Hello, > > count = 0 > threads = [] > > 10.times do |i| > threads[i] = Thread.new do > sleep(rand(0.1)) > Thread.current["mycount"] = count > count += 1 > end > end > > threads.each {|t| t.join; print t["mycount"], ", " } > > > For the code above, why the output numbers are random, rather than > from 0 to 9 by increasing? Because: (1) each thread sleeps for a random amount of time before capturing and incrementing the value of 'count'; but (2) you join each thread in the order in which they were started. Consider, for example, that threads[0] might sleep for 0.09 seconds, but threads[1] might sleep for 0.02 seconds. Hence threads[1] will capture a lower value than threads[0]. As has already been pointed out, this code is not threadsafe - occasionally, two threads may capture the same value of 'count'. That's because count += 1 is really a shorthand for count = count + 1 which is basically: - read value of count - add one to this value - store this value back to count Thread X could get as far as reading the value of 'count' before it is suspended; then thread Y could run, read the same value of 'count', and increment it. Then thread X will be re-scheduled, and also increment and save back the same value.
on 2010-03-10 09:24
2010/3/9 Brian Candler <b.candler@pobox.com>: >> count += 1 > > occasionally, two threads may capture the same value of 'count'. That's > - add one to this value > - store this value back to count > > Thread X could get as far as reading the value of 'count' before it is > suspended; then thread Y could run, read the same value of 'count', and > increment it. Then thread X will be re-scheduled, and also increment and > save back the same value. Brian, thank you for taking the time to do a more elaborate explanation. One additional thing: since Ruby's threads can actually return a value we can rewrite the original piece to this version, which is also thread safe: lock = Mutex.new count = 0 threads = (1..10).map do |i| Thread.new do sleep(rand(0.1)) lock.synchronize do count += 1 end end end threads.each do |th| puts th.value end Note that #synchronize returns the value returned by the block and by that way we return the result of incrementing as the thread's return value which is captured through Thread#value (which also joins the thread). Kind regards robert PS: We can make this even shorter, just for the fun of it - I don't really recommend that style: lock = Mutex.new count = 0 (1..10).map do |i| Thread.new do sleep(rand(0.1)) lock.synchronize do count += 1 end end end.each do |th| puts th.value end
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.