1

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

3 回答 3

3

本质上不是线程安全的,尽管它取决于正在做什么以及做什么。此外,实现——例如带有“绿色线程”的 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

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

于 2013-03-29T23:00:22.233 回答
2

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

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.

于 2013-03-30T00:06:32.353 回答
1

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

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

于 2013-03-29T22:59:46.153 回答