Имеет ли Ruby эквивалент Java для синхронизации ключевого слова? Я использую 1.9.1, и я не совсем понимаю, как это сделать.
Имеет ли Ruby эквивалент Java для синхронизации ключевого слова?
Ответ 1
У него нет ключевого слова synchronize
, но вы можете получить что-то очень похожее через класс Monitor
. Вот пример из книги Programming Ruby 1.8:
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
Принятый ответ не отражает, как работает synchronize
!
Вы можете просто прокомментировать synchronize do
и запустить принятый ответ script - вывод будет таким же: 200_000
!
Итак, вот пример, чтобы показать разницу между запуском с/без synchronize
блока:
Небезопасный пример:
#! /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
В выводе вы получите следующее:
before (1): 0
after (1): 1
before (2): 0
before (0): 0 <- !!
after (2): 2
after (0): 3 <- !!
Total: 3
Когда начался поток (0)
, count
был равен 0
, но после добавления +1
его значение было 3
.
Что здесь происходит?
Когда потоки начинаются, они видят начальное значение count
. Но когда каждый из них попытается добавить +1
, значение стало другим в результате параллельного вычисления. Без надлежащей синхронизации частичное состояние count
непредсказуемо.
Атомарность
Теперь мы называем эти операции 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
Вывод:
before (1): 0
after (1): 1
before (0): 1
after (0): 2
before (2): 2
after (2): 3
Total: 3
Теперь, используя блок synchronize
, мы обеспечиваем атомарность операции добавления.
но потоки, все еще выполняющиеся в случайном порядке (1- > 0- > 2)
Для подробного объяснения, вы можете продолжить чтение этой статьи.