全局Ruby变量中的局部变量是否是线程安全的?

时间:2013-03-29 22:51:19

标签: ruby-on-rails ruby multithreading

我有一个变量$ proxyManager,它是一个类ProxyManager的全局实例。 $ proxyManager = ProxyManager.new

我在这个类中有一个函数getProxy,多个线程多次调用它。在这个函数里面,我从数组中弹出一个项目(让我们假设这是线程安全的)。然后我将其标识符设置为当前时间,设置一个活动标志,然后将其返回。

proxy.identifier是否有可能在设置后在同一个线程中“更改”?例如,假设线程1将标识符设置为1,并且立即线程2执行相同的行并将其设置为2.这是否意味着线程1的标识符现在是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

3 个答案:

答案 0 :(得分:3)

它本身不是 线程安全的,尽管它依赖于正在做什么到什么。此外,实施 - 例如Ruby 1.8 MRI与“绿色线程”对比Ruby 2 MRI对JRuby等 - 将在竞争条件如何实现方面发挥作用。

请记住,竞争条件通常来自共享数据。变量并不重要(并且线程不会使用另一个线程局部变量,而不是递归方法将重用变量),但变量命名的对象很重要。 (注意:proxy本地变量,但proxy.instance 不是本地变量!)

竞争条件假设共享数据/对象

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

这里不是很令人兴奋,因为此时的结果 是相同的,但想象一下返回的对象(其中proxy_A和proxy_B 指的是)之间的 标识符变量的赋值(和线程可见性传播) - 破解代码。

即,假设上述内容已扩展:

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.  

当然,如果popFromGlobalArray保证返回不同的对象,则上述内容不适用,还有另一个问题 - 竞争条件依赖于精确的时间:

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

故事的道德:不要依靠运气。我已经简化了上面的内容,以显示线程之间即时数据可见性可能出现的竞争条件。但是,线程之间的数据可见性 - 即缺少内存屏障 - 使得这个问题比最初看起来要糟糕得多。

答案 1 :(得分:1)

每个线程定义局部变量,并且是线程安全的。

如果你的数组确实是原子的,每次线程进入这个函数时,代理变量都保证是一个不同的项,其他线程将无法覆盖标识符。

答案 2 :(得分:1)

如果 popFromGlobalArray()在多线程环境中正常运行并且保证不会多次返回同一个对象,并且代理类的实现不会在实例之间共享状态,其余部分功能应该没问题。您没有在不同线程上运行相同的数据,因此它们不会发生冲突。

如果您担心变量本身,则无需担心。每个方法调用定义本地,并且不同的线程将运行该方法的不同调用。他们不与当地人分享。

显然,具体细节可能会使其不那么真实,但这就是它通常的运作方式。