我使用以下Python代码来表明在多个线程上递增整数i
不是线程安全的:
>>> i = 0
>>> def increment_i():
... global i
... for j in range(1000): i += 1
...
>>> threads = [threading.Thread(target=increment_i) for j in range(10)]
>>> for thread in threads: thread.start()
...
>>> for thread in threads: thread.join()
...
>>> i
4858 # Not 10000
我试图将此代码转换为Ruby,期望得到类似的结果:
> $i = 0
> def increment_i()
> for j in 0...1000 do $i += 1 end
> end
> threads = (0...10).map { Thread.start { increment_i } }
> for thread in threads do thread.join end
如果我在1.8版本上运行这个Ruby代码,它确实与Python代码的行为相同,使$i
的值小于10000。
但是,在1.9和2.3上,$i
似乎总是设置为10000。
在Ruby 1.9及更高版本中,使用+=
实际上是线程安全的是递增一个整数吗?
答案 0 :(得分:4)
不幸的是,Ruby没有明确定义的内存模型,例如Java或C ++可以。因此,关于线程安全性和原子性的问题的 general 回答是:没有人知道,但你应该假设最坏的。
无法知道任何特定的Ruby实现将如何表现。正如您所注意到的,MRI和YARV表现不同。 (你假设差异是由于Ruby 1.8与Ruby 1.9有关,但这是错误的,差异是由于MRI与YARV的关系。)Rubinius可能表现出一种或另一种,或者完全不同的方式。 Opal,Topaz,Ruby + OMR,TruffleRuby,MagLev,MRuby,IronRuby,JRuby以及未来可能出现的任何其他内容相同。
然而,在这个特定的情况下,我们实际上可以得到一个比一般的更令人满意的答案"没有人知道":
$i += 1
只是
的语法糖$i = $i + 1
这显然是两个操作,因此显然是非原子的。它似乎适用于YARV的事实是由于私人内部实施细节无法保证,可能随时更改,恕不另行通知,并且可能存在或可能不存在于其他实施中。