我正在尝试多线程示例。我正在尝试使用以下代码生成竞争条件。但我总是得到相同(正确)的输出。
class Counter
attr_reader :count
def initialize
@count = 0
end
def increment
@count += 1
end
def decrement
@count -= 1
end
end
c = Counter.new
t1 = Thread.start { 100_0000.times { c.increment } }
t2 = Thread.start { 100_0000.times { c.increment } }
t1.join
t2.join
p c.count #200_0000
我能够在每个线程中使用少得多的迭代次数来观察Java中的竞争条件。是不是我没有运行足够多次来产生竞争条件,或者 +
/-
Ruby中的线程安全吗?我使用的是ruby 2.0.0p247
这是因为由于GIL,MRI Ruby线程并不真正并行(参见 这里),在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)
这是我在MRI 2.0上多次运行时看到的结果。
$ ruby check_then_set.rb =>剪毛......
$ ruby check_then_set.rb =>剪切...剪切......
$ ruby check_then_set.rb =>剪毛......
剪切...
有时同样的羊被剪了两次!
Ruby有一个 全球翻译锁。发生的一切 在Ruby中 基本上是同步的。所以你引用的问题是你在Java等低级语言中遇到的问题,其中两个线程可能会读取相同的值并相互冲突 +=
- 不是问题。
在哪里 Thread
class派上用场的是你编写的代码,它可以将事物带到Ruby之外,例如,使用文件或网络I / O,进行系统调用,或通过绑定与C库连接。
在Ruby中看到竞争条件的非常简单的方法:
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!
我也试图理解这一点,并在此代码中获得c.count的不同结果(从上面复制)。例如,我得到c.coint = 1,573,313或1,493,791等。看看代码,似乎c.count每次应该是2,000,000!
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 { 1_000_000.times { c.increment; c.tmp += 1 if c.count.even?; } }
t2 = Thread.start { 1_000_000.times { c.increment; c.tmp += 1 if c.count.even?; } }
t1.join
t2.join
p c.count # Varies e.g. 1,573,313 or 1,493,791 etc
p c.tmp # Also varies: 882,928 etc.
对于Java,您只能在异步线程中获取竞争条件。找到您需要的确切解决方案可能很有用。