Паттерн fan-out/fan-in используется для распараллеливания и координации задач (горутин). Особенно полезен, когда одну трудоемкую задача можно разделить на более мелкие подзадачи.
Cостоит из двух стадий:
1. Fan-out: задача делится на несколько мелких подзадач, которые затем выполняются конкурентно. Каждую подзадачу можно назначить отдельной горутине для параллельного выполнения.
2. Fan-in: результаты всех одновременно выполняемых подзадач собираются и объединяются в единый результат.
Реализация на Go
Используем комбинацию горутин и каналов в Go. Каждая подзадача назначается горутине, а каналы передают данные между горутинами. Стадия fan-in ожидает завершения всех подзадач, координируя их с помощью примитивов синхронизации, таких как sync.WaitGroup, или используя канал с сигналом о завершении каждой подзадачи.
Пример 1
"Тяжелую" задачу по вычислению факториала разбиваем на несколько подзадач. Для этого:
1. Создаем канал results в него будем записывать результаты вычислений.
2. Для каждого числа из списка nums запускаем горутину, которая вычисляет факториал и отправляет результат в канал out. Это "fan-out".
3. Запускаем горутину, которая ждёт завершения всех "вычислительных" горутин, используя sync.WaitGroup и затем закрывает канал out.
4. В главной горутине читаем результаты из канала out и выводим их.
Пример 2
Рассмотрим пример поинтереснее, когда у нас несколько воркеров. Подзадачей будет вычисление куба числа.
1. Определяем количество подзадач (numJobs) и воркеров (numWorkers), которые будут выполнять эти задачи.
2. Создаем два канала: один для подзадач (jobs), второй для результатов (results). Оба буферизованы с размером, равным количеству подзадач, чтобы можно было записывать в них, не блокируясь.
3. В цикле наполняем jobs подзадачами.
4. Запускаем воркер-горутины, которые будут обрабатывать подзадачи из канала jobs и отправлять результаты в канал results.
5. После того как все задачи отправлены, канал jobs закрываем - это сигнализирует воркерам, что больше задач не будет.
6. Запускаем горутину, которая ждет, пока счетчик WaitGroup не достигнет нуля, после чего закрываем канал results.
7. В главной горутине читаем результаты из канала results и выводим их. По мере чтения каждого результата, счетчик WaitGroup уменьшается.
В следующих статьях рассмотрим примеры таких задач, которые дают на собеседованиях.