Программирование на языке Ruby - [168]
> @bows_free -= 1
> puts "#{n} владеет смычком"
> puts "скрипок #@violins_free, смычков #@bows_free"
> end
> sleep rand(0)
> puts "#{n}: (...играет...)"
> sleep rand(0)
> puts "#{n}: Я закончил."
> @music.synchronize do
> @violins_free += 1
> @violin.signal if @violins_free == 1
> @bows_free += 1
> @bow.signal if @bows_free == 1
> end
> end
>end
>threads = []
>3.times {|i| threads << Thread.new { musician(i) } }
>threads.each {|t| t.join }
Мы полагаем, что это решение никогда не приводит к тупиковой ситуации, хотя доказать этого не сумели. Но интересно отметить, что описанный алгоритм не справедливый. В наших тестах оказалось, что первый скрипач играет чаще двух остальных, а второй чаще третьего. Выяснение причин такого поведения и его исправление мы оставляем читателю в качестве интересного упражнения.
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).
># Автор: 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
.
13.2.6. Тайм-аут при выполнении операций
Часто встречается ситуация, когда на выполнение операции отводится определенное максимальное время. Это позволяет избежать бесконечных циклов и более строго контролировать порядок работы. Подобная возможность очень полезна, в частности, в сетевых приложениях, где ответ от сервера может и не прийти.
Библиотека >timeout.rb
предлагает решение этой проблемы на основе потоков (см. листинг 13.6). С методом >timeout
ассоциирован выполняемый блок. Если истечет заданное число секунд, метод возбуждает исключение >TimeoutError
, которое можно перехватить с помощью >rescue
.
>require 'timeout.rb'
>flag = false
>answer = nil
>begin
> timeout(5) do
> puts "Хочу печенье!"
> answer = gets.chomp
> flag = true
> end
> rescue TimeoutError
> flag = false
>end
>if flag
> if answer == "cookie"
> puts "Спасибо! Хрум, хрум..."
> else
> puts "Это же не печенье!"
> exit
> end
>else
> puts "Эй, слишком медленно!"
> exit
>end
>puts "До встречи..."
13.2.7. Ожидание события
Часто один или несколько потоков следят за «внешним миром», а остальные выполняют полезную работу. Все примеры в этом разделе надуманные, но общий принцип они все же иллюстрируют.
В следующем примере прикладную задачу решают три потока. Четвертый поток каждые пять секунд просыпается, проверяет глобальную переменную >$flag
и, когда видит, что флаг поднят, пробуждает еще два потока. Это освобождает три рабочих потока от необходимости напрямую общаться с двумя другими и, возможно, от многочисленных попыток разбудить их.
>$flag = false
>work1 = Thread.new { job1() }
>work2 = Thread.new { job2() }
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Сейчас во многих школах, институтах и других учебных заведениях можно встретить компьютеры старого парка, уже отслужившие свое как морально, так и физически. На таких компьютерах можно изучать разве что Dos, что далеко от реалий сегодняшнего дня. К тому же у большинства, как правило, жесткий диск уже в нерабочем состоянии. Но и выбросить жалко, а новых никто не дает. Различные спонсоры, меценаты, бывает, подарят компьютер (один) и радуются, как дети. Спасибо, конечно, большое, но проблемы, как вы понимаете, этот компьютер в общем не решает, даже наоборот, усугубляет, работать на старых уже как-то не хочется, теперь просто есть с чем сравнивать.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.