Antoni C
Antoni C

Reputation: 43

Accessing thread variables of threads stored in instance variables

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

Answers (1)

max pleaner
max pleaner

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

Related Questions