Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 813
goyalankit Avatar asked Nov 14 '13 04:11

goyalankit


People also ask

Can you have a race condition in Javascript?

Yes, we can have race conditions in Node. js!

What is the root cause of race conditions?

Race conditions are most commonly associated with computer science and programming. They occur when two computer program processes, or threads, attempt to access the same resource at the same time and cause problems in the system. Race conditions are considered a common issue for multithreaded applications.


1 Answers

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!

like image 58
tihom Avatar answered Oct 09 '22 14:10

tihom