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() }
work3 = Thread.new { job3() }
thread4 = Thread.new { Thread.stop; job4() }
thread5 = Thread.new { Thread.stop; job5() }
watcher = Thread.new do
loop do
sleep 5
if $flag
thread4.wakeup
thread5.wakeup
Thread.exit
end
end