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