Имеет ли Ruby эквивалент Java для синхронизации ключевого слова?

Имеет ли Ruby эквивалент Java для синхронизации ключевого слова? Я использую 1.9.1, и я не совсем понимаю, как это сделать.

Ответ 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)

Для подробного объяснения, вы можете продолжить чтение этой статьи.