在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
作为一个整体意外地抢占。