【问题标题】:Are local variables in global Ruby variable thread-safe?全局 Ruby 变量中的局部变量是线程安全的吗?
【发布时间】:2015-08-07 08:54:10
【问题描述】:

我有一个变量 $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

【问题讨论】:

  • 是否有任何共享数据?也就是说,两个线程可以同时引用同一个代理对象吗?

标签: ruby-on-rails ruby multithreading


【解决方案1】:

它不是固有的线程安全的,尽管它取决于正在做什么做什么。此外,实施 - 例如带有“绿色线程”的 Ruby 1.8 MRI vs Ruby 2 MRI vs 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

故事的寓意:不要依赖运气。我已经简化了上面的内容,以显示线程之间数据的即时可见性可能发生的竞争条件。但是,线程之间的数据可见性——即缺乏内存栅栏——使这个问题比最初看起来要糟糕得多。

【讨论】:

    【解决方案2】:

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

    如果您担心变量本身,则不必担心。每个方法调用都定义了局部变量,并且不同的线程将运行方法的不同调用。他们不分享本地人。

    显然,细节可能会让这不太真实,但它通常是这样工作的。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-03-07
        • 2015-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多