Программирование на языке Ruby - [169]

Шрифт
Интервал

>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

>end

Если в какой-то момент выполнения метода >job, переменная >$flag станет равной >true, то в течение пяти секунд после этого потоки >thread4 и >thread5 гарантированно запустятся. После этого поток >watcher завершается.

В следующем примере мы ждем создания файла. Каждые 30 секунд проверяется его существование, и как только файл появится, мы запускаем новый поток. Тем временем остальные потоки занимаются своим делом. На самом деле ниже мы наблюдаем за тремя разными файлами.

>def waitfor(filename)

> loop do

>  if File.exist? filename

>   file_processor = Thread.new { process_file(filename) }

>   Thread.exit

>  else

>   sleep 30

>  end

> end

>end


>waiter1 = Thread.new { waitfor("Godot") }

>sleep 10

>waiter2 = Thread.new { waitfor("Guffman") }

>sleep 10

>headwaiter = Thread.new { waitfor("head") }


># Основной поток занимается другими делами...

Есть много ситуаций, когда поток должен ожидать внешнего события (например, в сетевых приложениях так бывает, когда сервер на другом конце соединения работает медленно или ненадежно).

13.2.8. Продолжение обработки во время ввода/вывода

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

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

Предположим, что итератор >predict_move генерирует вероятные ходы человека (и ответные ходы программы). Тогда в момент, когда человек сделает ход, не исключено, что у компьютера уже будет готов ответ.

>scenario = {} # Хэш ход-ответ.

>humans_turn = true

>thinking_ahead = Thread.new(board) do

> predict_move do |m|

>  scenario[m] = my_response(board,m)

>  Thread.exit if humans_turn == false

> end

>end


>human_move = get_human_move(board)

>humans_turn = false # Остановить поток.


># Теперь можно посмотреть, нет ли в хэше scenario хода,

># сделанного пользователем...

Конечно, настоящие шахматные программы работают не так.

13.2.9. Реализация параллельных итераторов

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

Рассмотрим следующий пример. Пусть >compose — имя магического метода, который выполняет композицию итераторов. Допустим еще, что у каждого объекта есть стандартный итератор >each и что каждый объект возвращает по одному элементу на каждой итерации.

>arr1 = [1, 2, 3, 4]

>arr2 = [5, 10, 15, 20]

>compose(arr1, arr2) {|a,b| puts "#{а} и #{b}" }


># Должно быть напечатано:

># 1 и 5

># 2 и 10

># 3 и 15

># 4 и 20

Можно было бы, конечно, использовать для этой цели >zip. Но если нужно более элегантное решение, при котором все элементы не будут храниться одновременно, то без потоков не обойтись. Такое решение представлено в листинге 13.7.

Листинг 13.7. Параллельные итераторы

>def compose(*objects)

> threads = []

> for obj in objects do

>  threads << Thread.new(obj) do |myobj|

>   me = Thread.current

>   me[:queue] = []

>   myobj.each {|x| me[:queue].push(x) }

>  end

> end


> list = [0]                     # Фиктивное значение, отличное от nil.

> while list.nitems > 0 do       # Еще есть не nil.

>  list = []

>  for thr in threads

>   list << thr[:queue].shift    # Удалить по одному из каждого.

>  end

>  yield list if list.nitems > 0 # He вызывать yield, если все равны nil.

> end

>end


>x = [1, 2, 3, 4, 5, 6, 7, 8]

>y = "    первый\n    второй\n    третий\n четвертый\n    пятый\n"

>z = %w[a b с d e f]


>compose(x, у, z) {|a,b,c| p [a, b, c] }


># Выводится:

># [1, "    первый\n", "a"]

># [2, "    второй\n", "b"]

># [3, "    третий\n", "c"]

># [4, " четвертый\n", "d"]

># [5, "     пятый\n", "e"]

># [6, nil, "f"]

># [7, nil, nil]

># [8, nil, nil]

Обратите внимание: мы не предполагаем, что все объекты имеют одно и то же число элементов. Если один итератор доходит до конца раньше остальных, то он будет генерировать значения >nil до тех пор, пока не закончит работу «самый длинный» итератор.

Конечно, можно написать и более общий метод, который на каждой итерации будет обрабатывать более одного элемента. (В конце концов, не все итераторы возвращают по одному значению за раз.) Можно было бы в первом параметре передавать число значений для каждого итератора.

Можно также пользоваться произвольными итераторами (а не только стандартным >each). Их имена можно было бы передавать в виде строк, а вызывать с помощью метода >send. Много чего еще можно придумать.

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

13.2.10. Параллельное рекурсивное удаление

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


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

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


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

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


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

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


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

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


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

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


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

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