Reputation: 43
This is expected,
t = Thread.new{
Thread.current[:rabbit] = 'white'
}
##### t[:rabbit] = white
But I can't understand this:
class Whatever
def initialize
@thd = Thread.new{
Thread.current[:apple] = 'whatever'
}
end
def apple
@thd[:apple]
end
def thd
@thd
end
end
I want to access these, why are they nil
?
Whatever.new.apple # nil
Whatever.new.thd[:apple] # nil
Whatever.new.thd.thread_variable_get(:apple) # nil
Why does this happen? How can I access @thd
Thread variables?
Upvotes: 3
Views: 56
Reputation: 26768
What you're seeing here is a race condition. You're attempting to read the thread variable before the body of the thread has been run.
Compare the following:
w = Whatever.new
w.apple
# => nil
w = Whatever.new
sleep 0.1
w.apple
# => "whatever"
Whether or not the thread body gets run in time with Whatever.new.apple
is pretty much random, it seems to happen 0.1% of the time for me, but this is probably different on other machines
1000.times.
map { Whatever.new.apple }.
each_with_object(Hash.new(0)) { |val, memo| memo[val] += 1 }
# => {nil=>999, "whatever"=>1}
2000.times.
map { Whatever.new.apple }.
each_with_object(Hash.new(0)) { |val, memo| memo[val] += 1 }
# => {nil=>1998, "whatever"=>2}
(note: I cannot try with a higher number of iterations because the large amount of thread spawning causes my IRB to run out of resources)
This relates to what I've heard described as the "number one rule of async", namely that you can't get the return value of an asynchronous method from a synchronous one. The usual way to handle this is with a "callback", which Ruby can do in the form of yield
/ blocks.
I recommend looking for a tutorial about how to do asynchronous programming in Ruby.
Upvotes: 4