Does ruby have the Java equivalent of synchronize keyword?
Does ruby have the Java equivalent of synchro开发者_开发技巧nize keyword? I am using 1.9.1 and I don't quite see an elegant way to do this.
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
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.
精彩评论