简体   繁体   中英

Are local variables in global Ruby variable thread-safe?

I have a variable $proxyManager, which is a global instance of a class ProxyManager. $proxyManager = ProxyManager.new

I have a function getProxy in this class, which is called many times by multiple threads. Inside this function, I am popping an item from an array (let's assume this is thread safe). I am then setting its identifier to the current time, setting an active flag, and returning it.

Is it possible for proxy.identifier to "change" in the same thread after it's been set? As an example, suppose Thread 1 sets the identifier to 1, and immediately Thread 2, executes the same line and sets it to 2. Does this mean Thread 1's identifier is now 2?

class ProxyManager
    def getProxy
        key = "proxy"
        proxy = popFromGlobalArray() #assume this operation is thread-safe/atomic

        proxy.identifier = Time.now.to_i
        proxy.active = 1
        return proxy
    end

end

It is not inherently thread-safe although it will depend upon exactly what is being done and to what . Also, the implementation - eg Ruby 1.8 MRI with "green threads" vs Ruby 2 MRI vs JRuby, etc - will play a role in how race conditions, if any, will materialize.

Remember that race conditions often result from shared data . The variables are not important (and a thread will not use another threads local variables any more than a recursive method will reuse variables), but the object named by the variables is important. (Note: proxy is a local variable but proxy.instance is not a local variable!)

Race condition assuming shared data/object :

proxy_A = popFromGlobalArray()
proxy_B = popFromGlobalArray() 
# assume same object was returned so that proxy_A.equal? proxy_B is true
proxy_A.identifier = Time.now.to_i
proxy_A.active = 1
proxy_B.identifier = Time.now.to_i # such that it is different
proxy_B.active = 1

This isn't very exciting here, because the outcome at this point is the same, but imagine if the returned object (of which proxy_A and proxy_B both refer to) has been used between the assignments (and thread visibility propagation) of the identifier variable - broken code.

That is, assume the above is expanded:

h = {}
# assume same object was returned so that proxy_A.equal? proxy_B is true
proxy_A.identifier = Time.now.to_i
h[proxy_A.identifier] = proxy_A    # i.e. used after return
proxy_B.identifier = Time.now.to_i # such that it is different
h[proxy_B.identifier] = proxy_B    # i.e. used after return
# now there may be an orphaned key/value.  

Of course, if popFromGlobalArray is guaranteed to return different objects then the above does not apply, but there is another issue - a race condition dependent upon precision of time:

proxy_A = popFromGlobalArray()
proxy_B = popFromGlobalArray()
# assume Time.now.to_i returns x for both threads
proxy_A.identifier = x
proxy_B.identifier = x
# and a lost proxy ..
h = {}
h[proxy_A.identifier] = proxy_A
h[proxy_B.identifier] = proxy_B

Moral of the story: don't rely on luck. I've simplified the above to show race conditions that can occur with instant visibility of data between threads. However, data visibility between threads - ie lack of memory fences - make this problem far worse than it may initially look.

local variables are defined per thread, and are thread safe.

if your array is indeed atomic, the proxy variable is guaranteed be a different item each time a thread enters this function, and other threads wont be able to overwrite the identifier.

If popFromGlobalArray() functions correctly in a multithreaded environment and is guaranteed not to return the same object more than once, and the implementation of the proxy class does not share state between instances, the rest of the function should be fine. You aren't operating on the same data on different threads, so they can't conflict.

If you're worried about the variables themselves, you needn't be. Locals are defined per method invocation, and different threads will be running different invocations of the method. They don't share locals.

Obviously the specifics can make this less true, but this is how it generally works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM