在(Ruby的最新版本)中增加一个整数(使用+ =)线程安全吗?

时间:2017-06-13 10:03:05

标签: ruby multithreading thread-safety

我使用以下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及更高版本中,使用+=实际上是线程安全的是递增一个整数吗?

1 个答案:

答案 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的事实是由于私人内部实施细节无法保证,可能随时更改,恕不另行通知,并且可能存在或可能不存在于其他实施中。