它本质上不是线程安全的,尽管它取决于正在做什么以及做什么。此外,实现——例如带有“绿色线程”的 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
故事的寓意:不要依赖运气。我已经简化了上面的内容,以显示线程之间数据的即时可见性可能发生的竞争条件。然而,线程之间的数据可见性——即缺乏内存栅栏——使这个问题比最初看起来要糟糕得多。