Одной из наиболее веских причин для создания программного обеспечения на JVM является превосходная поддержка Java для параллельного программирования. Книга Брайана Гетца Java Concurrency in Practice широко считается одной из лучших книг по параллельному программированию и обязательна к прочтению для Java-программистов. Но с развитием функционального программирования и ростом популярности таких языков, как Erlang, Scala, Go и, конечно же, Clojure, на первый план вышли новые модели параллелизма.

Хотя все механизмы параллелизма Java доступны в Clojure, Clojure через core.async, как и Go, предоставляет первоклассный объект, похожий на очередь, называемый каналом, в качестве центрального элемента для параллельного программирования. Clojure core.async также имеет то преимущество, что он (в основном) доступен в Clojurescript.

Как и в случае перехода от объектно-ориентированной парадигмы Java к функциональной парадигме Clojure, понимание и способность использовать модель параллелизма на основе каналов может занять время. Ниже приведены несколько шаблонов, которые возникли из моего собственного, все еще относительно нового, опыта работы с core.async. Я надеюсь, что эти шаблоны помогут лучше понять, как использовать великолепную модель параллельного программирования Clojure.

Одновременно выполняйте блокирующие задачи с глобальным таймаутом

Первый шаблон выражается в виде двух функций: do-blocking-tasks и «accum-blocking-task-results». Эти функции могут одновременно выполнять ряд блокирующих задач, а затем собирать результаты в карту ввода для результата. Сбор осуществляется путем перебора нескольких вызовов alts !!, а канал «timeout» используется для сигнализации глобального тайм-аута. Под глобальным тайм-аутом я подразумеваю, что мы пытаемся обработать ВСЕ задачи в течение заданного времени, а не тайм-аут каждой задачи отдельно. В этом случае мы не генерируем исключение по истечении времени ожидания, а просто возвращаем то, что накопили. Остальное я позволю коду и комментариям объяснить.

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

Регулирование серии процессов

Следующий паттерн показан на несколько надуманном примере. Это намного проще, чем выше, потому что нет глобального тайм-аута. Идея состоит в том, что у вас есть ряд процессов, которые могут блокироваться, например, использование результатов выборки чего-либо с одного URL-адреса для извлечения чего-либо с другого URL-адреса, и вы хотите «конвейеризовать» их вместе, контролируя количество параллельных процессов для каждого шага. . В этом случае мы сначала получаем персонажа «Звездных войн» из внешнего API, а затем делаем второй запрос, чтобы получить название его / ее / ее вида. В этом примере нет реальной причины для разных уровней параллелизма для каждого запроса, но здесь сделано, чтобы проиллюстрировать, что это возможно.

Я знаю, о чем вы думаете ... Разве Лобот не отчасти Дроид? И чем это полезно? Я могу ответить только на первый вопрос (но я был бы упущен, если бы не упомянул, как здорово, что вид Йоды указан как вид Йоды). В Конторе мы выполняем некоторую асинхронную обработку изображений, которая требует больших затрат памяти. Например, мы можем считывать изображение в память для сбора метаданных, затем изменять его размер, а затем применять водяной знак. Это идеальная ситуация для конвейерной обработки, но без возможности регулировать количество параллельных процессов мы рискуем вызвать ошибки OutOfMemory и привести к сбою наших сервисов. Так что в этом случае конвейеры core.async - очень полезный инструмент.

Вот и все. В этих примерах есть ряд других лакомых кусочков, которые стоит изучить дополнительно, и эти шаблоны, конечно, можно объединить для создания серии конвейерных процессов блокировки, которые можно регулировать с помощью глобального времени t. :-). Наконец, если вы еще этого не сделали, очень полезно использовать все документы и видео, на которые есть ссылки со страницы core.async на github.