Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
955 views
in Technique[技术] by (71.8m points)

multithreading - Why no race condition in Ruby

I was experimenting with multithreading examples. I am trying to produce a race condition using the following code. But I am always getting the same (correct) output.

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

I am able to observe the race condition in Java using much less number of iterations in each thread. Is it that I am not running it enough number of times to produce a race condition, or +/- are Thread safe in Ruby? I am using ruby 2.0.0p247

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is because MRI Ruby threads are not really parallel due to GIL (see here), at CPU level they are executed one at a time.

Each command in a thread is executed one at a time hence @count in each thread is always updated correctly.

Race condition can be simulated by adding another variable like:

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

A nice example of race condition is given here, copied below for completeness

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)

Here's the result I see from running this on MRI 2.0 several times.

$ ruby check_then_set.rb => shearing...

$ ruby check_then_set.rb => shearing... shearing...

$ ruby check_then_set.rb => shearing... shearing...

Sometimes the same sheep is being shorn twice!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...