20

Possible Duplicate:
Does ruby have the Java equivalent of synchronize keyword?

In Java we can make a method 'synchronized' by just using the 'synchronized' keyword in the function definition.

How do we do it in Ruby?

Community
  • 1
  • 1
Imran Omar Bukhsh
  • 7,849
  • 12
  • 59
  • 81

1 Answers1

22

Synchronize keyword is not present in Ruby. Alternatively just can just wrap the method call to Mutex (i.e. fancy word for Lock).

Create new shared Mutex for that class (everyone must use the same Mutex (lock) to access the same variables):

NUM_THREADS = 4

class Foo
  def initialize
    @my_mutex = Mutex.new
    @my_val = 0 # should be private
  end

  def synchronize(&block)
    # to see what it does without the mutex in this example:
    # 1) comment this line
    @my_mutex.synchronize(&block)
    # 2) uncomment this line
    # yield
  end

  def current_value
    synchronize do
      @my_val
    end
  end

  def modify
    # the value should be 0 before and 0 after, if the Mutex is used correctly
    synchronize do
      @my_val += 1
      sleep 0.25
      @my_val -= 1
      sleep 0.25
    end
  end
end

foo = Foo.new

threads = []

# spawn N threads, all trying to change the value
threads += (1..NUM_THREADS).map { |i|
  Thread.new {
    puts "thread [##{i}]: modifying"
    foo.modify
  }
}

# spawn checking thread
threads << Thread.new {
  # print the value twice as fast as the other threads are changing it, so we are more likely to stumble upon wrong state
  (NUM_THREADS * 2).times {
    puts "thread [check]: checking..."
    raise if foo.current_value != 0 # locking failed, crash
    sleep 0.25
  }
}

threads.map { |t| t.join } # wait for all threads

puts "even though it took a while longer, it didn't crash, everyone is happy, managers didn't fire me... it worked!"

See http://apidock.com/ruby/Mutex

The program runs longer, because of all those locking. Speed depends on your ruby implementation (e.g. green threads, native threads..) and number of cores. If you disable mutex in this example above, the program crashes right away, because of the raise guard in checking thread. Note that the checking thread must use the mutex too, because otherwise it would still be able to read the value in the middle of change by other threads. I.e. everyone must use the same mutex to access that variable.

To make around the lack of synchronized keyword, I defined method synchronize which uses the class defined Mutex.

Dalibor Filus
  • 1,140
  • 9
  • 19
  • What about this http://stackoverflow.com/questions/3208462/does-ruby-have-the-java-equivalent-of-synchronize-keyword? – Imran Omar Bukhsh Dec 30 '12 at 13:36
  • 13
    The first code sample is good, but `Mutex.new.synchronize` is useless. Because you don't keep a reference to the newly created Mutex, no other thread can ever try to lock it, so it will never exclude any thread from the critical region (which is the whole point of mutexes). – Alex D Dec 30 '12 at 14:42
  • I'm not sure if it's true but what @AlexD said makes total sense! – Ismael Abreu Dec 30 '12 at 16:52
  • @AlexD I just stumbled upon this answer and updated it to reflect your comment, because you were right. – Dalibor Filus Apr 29 '14 at 09:55
  • Looks much better now! – Alex D Apr 29 '14 at 11:26
  • Hmm.. I just comment out `@my_mutex.synchronize do` and answer was the same - `0`. So it not very clear answer. The main thing it in which order code will be executed. – skywinder Jun 15 '15 at 06:53
  • I add another answer with explanation how `synchronize` works. http://stackoverflow.com/a/30840316/1698467 Hope it helps to feel the difference. – skywinder Jun 15 '15 at 08:30
  • @skywinder if I commented out the synchronize blocks, the results would be mixed. That might depend on which Ruby runtime you are using though. Each one handles threads differently. The results should be pretty random and inconsistent on all of them. Try running it multiple times. – Dalibor Filus Feb 14 '17 at 10:31
  • what run time are you using? I'm getting the same results as @skywinder. I even added a sleep and in between copying the my_value to a temp and using it. – Bren Feb 09 '18 at 23:03
  • @skywinder You guys were right, I don't know what I was thinking. The original code was completely wrong. I've replaced it with different use case, which is working and shows the way Mutex works in real life. Hope it's much better now. – Dalibor Filus Mar 20 '18 at 11:31
  • @Bren The code is now replaced by different use case which truly breaks when the Mutex is not used. Ruby implementation now defines just the speed of the code. – Dalibor Filus Mar 20 '18 at 11:32