Мы полагаем, что это решение никогда не приводит к тупиковой ситуации, хотя доказать этого не сумели. Но интересно отметить, что описанный алгоритм не справедливый. В наших тестах оказалось, что первый скрипач играет чаще двух остальных, а второй чаще третьего. Выяснение причин такого поведения и его исправление мы оставляем читателю в качестве интересного упражнения.
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 с помощью монитора