Does ruby have the Java equivalent of synchronize keyword? I am using 1.9.1 and I don't quite see an elegant way to do this.
问题:
回答1:
It doesn't have the synchronize
keyword, but you can get something very similar via the Monitor
class. Here's an example from the Programming Ruby 1.8 book:
require 'monitor'
class Counter < Monitor
attr_reader :count
def initialize
@count = 0
super
end
def tick
synchronize do
@count += 1
end
end
end
c = Counter.new
t1 = Thread.new { 100_000.times { c.tick } }
t2 = Thread.new { 100_000.times { c.tick } }
t1.join; t2.join
c.count → 200000
回答2:
The accepted answer doesn't represent how synchronize
works!
You can just comment out synchronize do
and run accepted answer's script - output will be the same: 200_000
!
So, here is an example, to show the difference between running with/without synchronize
block:
Not thread safe example:
#! /usr/bin/env ruby
require 'monitor'
class Counter < Monitor
attr_reader :count
def initialize
@count = 0
super
end
def tick i
puts "before (#{ i }): #{ @count }"
@count += 1
puts "after (#{ i }): #{ @count }"
end
end
c = Counter.new
3.times.map do |i|
Thread.new do
c.tick i
end
end.each(&:join)
puts c.count
In the output you will get sometihing like that:
before (1): 0
after (1): 1
before (2): 0
before (0): 0 <- !!
after (2): 2
after (0): 3 <- !!
Total: 3
When the thread (0)
started, count
was equal to 0
, but after adding +1
its value was 3
.
What happens here?
When the threads are starting they see the initial value of count
. But when each of them, try to add +1
, the value became different as result of the parallel computation. Without a proper synchronization, the partial state of count
is unpredictable.
Atomicity
Now we call these operations atomic:
#! /usr/bin/env ruby
require 'monitor'
class Counter < Monitor
attr_reader :count
def initialize
@count = 0
super
end
def tick i
synchronize do
puts "before (#{ i }): #{ @count }"
@count += 1
puts "after (#{ i }): #{ @count }"
end
end
end
c = Counter.new
3.times.map do |i|
Thread.new do
c.tick i
end
end.each(&:join)
puts c.count
Output:
before (1): 0
after (1): 1
before (0): 1
after (0): 2
before (2): 2
after (2): 3
Total: 3
Now, by using synchronize
block, we ensure the atomicity of the add operation.
but threads still running in random order (1->0->2)
For detailed explanation, your can continue reading this article.