在Ruby中,为什么“我是真的+;”1 end`不是线程安全的吗?

在Ruby中,为什么“我是真的+;”1 end`不是线程安全的吗?,ruby,multithreading,mri,Ruby,Multithreading,Mri,据此,i+=1在MRI Ruby中是线程安全的,因为抢占只发生在函数调用结束时,而不是在i+=1之间 下面的可重复测试表明这是正确的: 但是为什么而true do i+=1 end不是线程安全的,如下面的第二个测试所示,当thread1仍在执行而true do i+=1 end时,thread1被thread2抢占 请帮忙 以下是代码参考: 测试一: 100.times do i = 0 1000.times.map do Thread.new {1000.times {i

据此,
i+=1
在MRI Ruby中是线程安全的,因为抢占只发生在函数调用结束时,而不是在
i+=1
之间

下面的可重复测试表明这是正确的:

但是为什么
而true do i+=1 end
不是线程安全的,如下面的第二个测试所示,当thread1仍在执行
而true do i+=1 end
时,thread1被thread2抢占

请帮忙

以下是代码参考:

测试一:

100.times do
  i = 0
  1000.times.map do
    Thread.new {1000.times {i += 1}}
  end.each(&:join)
  puts i
end
测试二:

t1 = Thread.new do
  puts "#{Time.new} t1 running"
  i = 0
  while true do i += 1 end
end

sleep 4

t2 = Thread.new do
  puts "#{Time.new} t2 running"
end

t1.join
t2.join
根据这篇文章,
i+=1
在MRI中是线程安全的

不完全是。这篇博文指出,方法调用在MRI中是有效的线程安全的

缩写赋值
i+=1
是对以下内容的语法修饰:

i = i + 1
所以我们有一个赋值
i=…
和一个方法调用
i+1
。根据博文,后者是线程安全的。但它也表示线程切换可以在返回方法结果之前发生,即在结果重新分配给
i
之前:

i = i + 1
#  ^
# here
不幸的是,从Ruby内部进行演示并不容易

但是,我们可以挂接到
Integer#+
并随机要求线程调度程序将控制传递给另一个线程:

module Mayhem
  def +(other)
    Thread.pass if rand < 0.5
    super
  end
end
输出:

5
7
6
4
4
8
4
5
6
7
10
10
10
10
10
10
10
10
10
10
如果您想要线程安全的代码,不要依赖于实现细节(这些细节可能会改变)。在上面的示例中,您可以在调用中包装敏感部分:

输出:

5
7
6
4
4
8
4
5
6
7
10
10
10
10
10
10
10
10
10
10

第一个测试没有证明任何东西,除了在100次测试中没有发生抢占。第二个示例如何显示
i+=1
不是线程安全的?@Stefan源代码附加。通过显示大量的确认无法证明任何东西。“反过来证明了这一点。”陈胜文在博客中说,在MRI中,中断标志在从方法返回之前被检查,并得出结论,方法调用因此是原子的。换句话说:原子性在从方法返回时结束。这意味着你的
while
没有包含在内。事实上
i+1
是一个方法调用,即它等于i+(1),这对我帮助很大。它与C等编译语言有很大的不同。公平地说,这篇博文说“C中实现的方法是原子的”,所以它不适用于我们重写的方法。但这个例子清楚地表明,MRI并没有试图在原子上保留像
i=…
这样的较大语句。(更不用说循环条件了)我知道。在重写内部使用
rand
可能会导致检查中断标志,从而使
i=i+1
作为一个整体意外地抢占。