Программирование на языке 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).

Листинг 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.

13.2.6. Тайм-аут при выполнении операций

Часто встречается ситуация, когда на выполнение операции отводится определенное максимальное время. Это позволяет избежать бесконечных циклов и более строго контролировать порядок работы. Подобная возможность очень полезна, в частности, в сетевых приложениях, где ответ от сервера может и не прийти.

Библиотека >timeout.rb предлагает решение этой проблемы на основе потоков (см. листинг 13.6). С методом >timeout ассоциирован выполняемый блок. Если истечет заданное число секунд, метод возбуждает исключение >TimeoutError, которое можно перехватить с помощью >rescue.

Листинг 13.6. Пример тайм-аута

>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() }


Рекомендуем почитать
Графика DirectX в Delphi

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Вторая жизнь старых компьютеров

Сейчас во многих школах, институтах и других учебных заведениях можно встретить компьютеры старого парка, уже отслужившие свое как морально, так и физически. На таких компьютерах можно изучать разве что Dos, что далеко от реалий сегодняшнего дня. К тому же у большинства, как правило, жесткий диск уже в нерабочем состоянии. Но и выбросить жалко, а новых никто не дает. Различные спонсоры, меценаты, бывает, подарят компьютер (один) и радуются, как дети. Спасибо, конечно, большое, но проблемы, как вы понимаете, этот компьютер в общем не решает, даже наоборот, усугубляет, работать на старых уже как-то не хочется, теперь просто есть с чем сравнивать.


DirectX 8. Начинаем работу с DirectX Graphics

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Симуляция частичной специализации

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.