litbaza книги онлайнРазная литератураCrystal Programming. Введение на основе проекта в создание эффективных, безопасных и читаемых веб-приложений и приложений CLI - Джордж Дитрих

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 28 29 30 31 32 33 34 35 36 ... 75
Перейти на страницу:
class="p1">    sleep 3

    channel1.send 1

end

spawn do

    puts "Starting fiber 2"

    sleep 1

    channel2.send 2

end

select

when v = channel1.receive

    puts "Received #{v} from channel1"

when v = channel2.receive

    puts "Received #{v} from channel2"

end

Этот пример выводит следующее:

Starting fiber 1

Starting fiber 2

Received 2 from channel2

Здесь оба волокна начинают выполняться более или менее одновременно, но поскольку у второго волокна более короткий период сна и он завершается первым, это приводит к тому, что ключевое слово select печатает значение из этого канала и затем завершает работу. Обратите внимание, что ключевое слово select действует аналогично одиночному каналу channel.receive в том смысле, что оно блокирует основное волокно, а затем продолжает работу после получения значения из любого канала. Кроме того, мы могли бы обрабатывать несколько итераций, поместив ключевое слово select в цикл вместе с методом timeout, чтобы избежать вечной блокировки. Давайте расширим предыдущий пример, чтобы продемонстрировать, как это работает. Во-первых, давайте добавим переменную channel3, аналогичную двум другим, которые у нас уже есть. Далее давайте создадим еще одно волокно, которое отправит значение в наш третий канал. Например, взгляните на следующее:

spawn do

    puts "Starting fiber 3"

    channel3.send 3

end

Наконец, мы можем переместить наше ключевое слово select в цикл:

loop do

  select

  when v = channel1.receive

    puts "Received #{v} from channel1"

  when v = channel2.receive

    puts "Received #{v} from channel2"

  when v = channel3.receive

    puts "Received #{v} from channel3"

  when timeout 3.seconds

    puts "Nothing left to process, breaking out"

    break

  end

end

Эта версия ключевого слова select аналогична первой, но мы добавили к ней два новых предложения. Один считывает значение из третьего канала, а другой выйдет из цикла, если ни по одному каналу в течение трех секунд не будут получены данные. Вывод этой программы следующий:

Starting fiber 1

Starting fiber 2

Starting fiber 3

Received 3 from channel3

Received 2 from channel2

Received 1 from channel1

Nothing left to process, breaking out

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

Ключевое слово select не ограничивается только получением значений. Его также можно использовать при их отправке. Возьмем эту программу в качестве примера:

spawn_receiver = true

channel = Channel(Int32).new

if spawn_receiver

  spawn do

    puts "Received: #{channel.receive}"

  end

end

  spawn do

  select

  when channel.send 10

    puts "sent value"

  else

    puts "skipped sending value"

  end

end

Fiber.yield

Запуск этого как есть дает следующий результат:

sent value

Received: 10

Установка флага spawn_receiver в значение false и его повторный запуск приводит к пропущенному значению отправки. Причина разницы в выводе связана с поведением send в сочетании с предложением else ключевого слова select. select проверит каждое предложение if на наличие того, которое не будет блокироваться при выполнении. Однако в этом случае отправляйте блоки, поскольку нет волокна, ожидающего значения, поэтому предложение else будет выполнено, поскольку ни одно другое предложение не может быть выполнено без блокировки. Поскольку принимающее волокно не было создано, выполняется последний путь, что приводит к пропуску сообщения. В другом сценарии ожидающий получатель не позволяет блокировать отправку.

Хотя использование каналов и волокон для сигнализации о завершении единицы работы является одним из вариантов их использования, это не единственный вариант использования. Эти две концепции, а также select, можно объединить для создания довольно мощных шаблонов, таких как разрешение одновременного выполнения только определенного количества волокон, координация состояния между несколькими волокнами и каналами или обработка нескольких независимых фрагментов данных. работать одновременно. Последний имеет дополнительное преимущество: скорее всего, он уже настроен для обработки многопоточных рабочих процессов, поскольку каждое волокно может обрабатываться в отдельном потоке.

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

Преобразование нескольких файлов одновременно

На данный момент приложение поддерживает файловый ввод, но только из одного файла. Допустимым вариантом использования может быть предоставление нескольких файлов и создание нового файла с преобразованными данными для каждого из них. Учитывая, что логика преобразования привязана к IO, одновременное выполнение этого имеет смысл и должно привести к повышению производительности.

Причина, по которой логика, связанная с IO, и параллелизм так хорошо сочетаются друг с другом, заключается в планировщике Crystal. Когда волокно достигает точки своего выполнения, когда оно зависит от некоторой части данных из IO, планировщик может легко отложить это волокно в сторону до тех пор, пока не поступят эти данные.

Более конкретным примером этого в действии было бы рассмотрение того, как функционирует стандартная библиотека HTTP::Server. Каждый запрос обрабатывается в отдельном волокне. Из-за этого, если во время обработки запроса необходимо выполнить еще один HTTP-запрос, например, для получения данных из внешнего API, Crystal сможет продолжать обрабатывать другие запросы, ожидая возвращения данных через IO сокет.

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

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

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

• Найдите способ сообщить CLI, что он должен обрабатывать файлы в режиме нескольких файлов.

• Определить новый метод, который будет обрабатывать каждый файл из ARGV.

Первое требование можно удовлетворить, поддерживая опцию CLI --multi, которая переведет его в правильный режим. Второе требование также простое,

1 ... 28 29 30 31 32 33 34 35 36 ... 75
Перейти на страницу:

Комментарии
Минимальная длина комментария - 20 знаков. Уважайте себя и других!
Комментариев еще нет. Хотите быть первым?