这是因为 MRI Ruby 线程由于 GIL(参见 此处 )而并非真正并行,在 CPU 级别它们一次只执行一个。
线程中的每个命令一次执行一个,因此 @count 每个线程始终正确更新。
可以通过添加另一个变量来模拟竞争条件,例如:
class Counter
attr_accessor :count, :tmp
def initialize
@count = 0
@tmp = 0
end
def increment
@count += 1
end
end
c = Counter.new
t1 = Thread.start { 1000000.times { c.increment; c.tmp += 1 if c.count.even?; } }
t2 = Thread.start { 1000000.times { c.increment; c.tmp += 1 if c.count.even?; } }
t1.join
t2.join
p c.count #200_0000
p c.tmp # not 100_000, different every time
class Sheep
def initialize
@shorn = false
end
def shorn?
@shorn
end
def shear!
puts "shearing..."
@shorn = true
end
end
sheep = Sheep.new
5.times.map do
Thread.new do
unless sheep.shorn?
sheep.shear!
end
end
end.each(&:join)
i = 0
2.times do
Thread.new do
30_000_000.times do # this should take more than 100ms
a = i + 1
i = a
end
end
end
puts i # the value will always be different
没有竞争条件的示例:
i = 0
2.times do
Thread.new do
10_000.times do # this should take less than 100ms
a = i + 1
i = a
end
end
end
puts i # 20000, always!
.
i = 0
2.times do
Thread.new do
30_000_000.times do # it doesn't matter how much time it takes
i += 1
end
end
end
puts i # 60000000, always!