Книга: Программирование на языке Ruby

13.2.5. Другие способы синхронизации

13.2.5. Другие способы синхронизации

Еще один механизм синхронизации - это монитор, который в Ruby реализован в библиотеке monitor.rb. Это более развитый по сравнению с мьютексом механизм, основное отличие состоит в том, что захваты одного и того же мьютекса не могут быть вложенными, а монитора — могут.

Тривиальный случай возникновения такой ситуации вряд ли возможен. В самом деле, кто станет писать такой код:

@mutex = Mutex.new
@mutex.synchronize do
 @mutex.synchronize do
  #...
 end
end

Но нечто подобное может произойти в сложной программе (или при рекурсивном вызове метода). Какова бы ни была причина, последствием будет тупиковая ситуация. Уход от нее — одно из достоинств модуля-примеси Monitor.

@mutex = Mutex.new
def some_method
 @mutex.synchronize do
  #...
  some_other_method # Тупиковая ситуация!
 end
end
def some_other_method
 @mutex.synchronize do
  #...
 end
end

Модуль-примесь Monitor обычно применяется для расширения объекта. Для создания условной переменной предназначен метод new_cond.

Класс ConditionVariable в библиотеке monitor.rb дополнен по сравнению с определением в библиотеке thread. У него есть методы wait_until и wait_while, которые блокируют поток в ожидании выполнения условия. Кроме того, возможен тайм-аут при ожидании, поскольку у метода wait имеется параметр timeout, равный количеству секунд (по умолчанию nil).

Поскольку примеры работы с потоками у нас кончаются, то в листинге 13.5 мы предлагаем реализацию классов Queue и SizedQueue с помощью монитора. Код приводится с разрешения автора, Шуго Маэда (Shugo Maeda).

Листинг 13.5. Реализация класса Queue с помощью монитора

# Автор: Shugo Maeda
require 'monitor'
class Queue
 def initialize
  @que = []
  @monitor = Monitor.new
  @empty_cond = @monitor.new_cond
 end
 def enq(obj)
  @monitor.synchronize do
   @que.push(obj)
   @empty_cond.signal
  end
 end
 def deq
  @monitor.synchronize do
   while @que.empty?
    @empty_cond.wait
   end
   return @que.shift
  end
 end
end
class SizedQueue < Queue
 attr :max
 def initialize(max)
  super()
  @max = max
  @full_cond = @monitor.new_cond
 end
 def enq(obj)
  @monitor.synchronize do
   while @que.length >= @max
    @full_cond.wait
   end
   super(obj)
  end
 end
 def deq
  @monitor.synchronize do
   obj = super
   if @que.length < @max
    @full_cond.signal
   end
   return obj
  end
 end
 def max=(max)
  @monitor.synchronize do
   @max = max
   @full_cond.broadcast
  end
 end
end

Еще один вариант синхронизации (двузначную блокировку со счетчиком) предлагает библиотека sync.rb. В ней определен модуль Sync_m, который можно применять вместе с ключевыми словами include и extend (как и Mutex_m). Этот модуль содержит методы locked?, shared?, exclusive?, lock, unlock и try_lock.

Оглавление книги


Генерация: 1.162. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз