我正在尝试多线程示例。我正在尝试使用以下代码生成竞争条件。但我总是得到相同(正确)的输出。
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
答案 0 :(得分:13)
这是因为MRI Ruby线程由于GIL而不是真正的并行(参见here),在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
一个很好的竞争条件示例是here,下面复制完整性
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 =>剪切... 剪切...
有时候同样的羊被剪了两次!
答案 1 :(得分:1)
Ruby有一个global interpreter lock。在Ruby 中发生的所有事情基本上是同步的。因此,您在Java等低级语言中遇到的引用问题 - 其中两个线程可能读取相同的值并在+=
上相互冲突 - 不是问题。
Thread
类派上用场的地方在于编写代码以获取Ruby之外的内容,例如,使用文件或网络I / O,进行系统调用,或通过绑定与C库连接。
答案 2 :(得分:1)
这将归功于Ruby 2.0的 Global Interpreter Lock 。
简而言之,由于Ruby解释器的底层实现,任何非IO的操作(例如文件读/写)都将同步发生。
请参阅:
答案 3 :(得分:1)
在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!
答案 4 :(得分:0)
我也试图理解这一点,并且在此代码中从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.
答案 5 :(得分:-1)
对于Java,您只能在异步线程中获取竞争条件。找到您需要的确切解决方案可能很有用..