gpt4 book ai didi

ruby - 在 Ruby 中,为什么 `while true do i += 1 end` 不是线程安全的?

转载 作者:太空宇宙 更新时间:2023-11-03 17:19:59 25 4
gpt4 key购买 nike

根据这个post , i += 1 在 MRI Ruby 中是线程安全的,因为抢占只发生在函数调用结束时,而不是 i += 1 之间的某个地方。

下面的可重复测试表明这是真的: repeatable test

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

second test

请帮忙。

引用代码如下:

测试一:

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

最佳答案

According to this post, i += 1 is thread safe in 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

如果 MRI 确保整个 i += 1 语句的线程安全,则上述内容不应有任何影响。但它确实:

Integer.prepend(Mayhem)

10.times do
i = 0
Array.new(10) { Thread.new { i += 1 } }.each(&:join)
puts i
end

输出:

5
7
6
4
4
8
4
5
6
7

如果你想要线程安全的代码,不要依赖实现细节(那些可以改变)。在上面的示例中,您可以将敏感部分包装在 Mutex#synchronize 中。调用:

Integer.prepend(Mayhem)

m = Mutex.new

10.times do
i = 0
Array.new(10) { Thread.new { m.synchronize { i += 1 } } }.each(&:join)
puts i
end

输出:

10
10
10
10
10
10
10
10
10
10

关于ruby - 在 Ruby 中,为什么 `while true do i += 1 end` 不是线程安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56767842/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com