Параллельные вычисления

CSP и каналы: Go

2012 год. Google запускает Go в production. Проблема: тысячи сервисов, миллиарды запросов, команды ненавидят сложность многопоточности в Java и C++. Решение - сделать конкурентность настолько дешёвой, чтобы она стала дефолтным способом написания кода.

  • Docker и Kubernetes написаны на Go - оба интенсивно используют горутины и каналы для управления контейнерами
  • Cloudflare обрабатывает 55 миллионов HTTP запросов в секунду на Go - горутины позволяют держать соединение открытым дёшево
  • CockroachDB использует Go каналы для репликации и Raft consensus - тысячи горутин на ноду

Горутины: дешёвые потоки Go runtime

Горутина запускается с 2-8 KB стека (OS-поток - 1-8 MB). Go runtime мультиплексирует тысячи горутин на пул OS-потоков через планировщик M:N. Создание горутины занимает микросекунды и несколько KB - это меняет то, как можно писать конкурентный код.

Go runtime планировщик использует алгоритм work-stealing: незанятые OS-потоки воруют горутины из очередей занятых потоков. Это обеспечивает равномерную загрузку CPU без явного управления пулами потоков.

Сколько OS-потоков использует программа на Go с 10 000 горутин?

Каналы: типизированные трубы для данных

Мантра Go: 'Don't communicate by sharing memory; share memory by communicating.' Канал - типизированная очередь между горутинами. Небуферизованный канал синхронизирует отправителя и получателя. Буферизованный - позволяет отправить N элементов без блокировки.

Закрытие канала - способ сообщить получателям об окончании данных. После close(ch) чтение из канала возвращает нулевое значение и false. Запись в закрытый канал вызывает panic. Правило: закрывает канал только отправитель.

Что произойдёт при записи в закрытый канал в Go?

Select: мультиплексирование каналов

select позволяет горутине ждать на нескольких каналах одновременно. Выполняется первый готовый case. Если несколько готовы одновременно - выбор случаен. default делает select неблокирующим.

Два case в select одновременно готовы к выполнению. Что выберет Go runtime?

Fan-in / Fan-out: паттерны параллельной обработки

Fan-out: один входной канал, несколько горутин-обработчиков. Fan-in: несколько каналов сливаются в один. Комбинация - стандартный паттерн для параллельной обработки потоков данных.

Docker и Kubernetes интенсивно используют Go каналы: Docker streaming build output, Kubernetes event processing в контроллерах. Каждый этап pipeline - горутина, соединённая каналами. Backpressure автоматический - медленный consumer замедляет producer через блокировку канала.

В чём ключевое преимущество Fan-out через каналы перед явным thread pool?

Ключевые идеи

  • Горутина стоит 2-8 KB и микросекунды - можно запускать миллионы без overhead OS-потоков.
  • Каналы синхронизируют данные и управление: небуферизованный = рандеву, буферизованный = очередь.
  • Fan-out/fan-in через каналы - идиоматичный Go паттерн для параллельной обработки с backpressure.

Связанные темы

CSP в Go - альтернатива другим моделям конкурентности:

  • Actor Model (Erlang/Akka) — Схожая идея - не разделять память, но в акторах каждый имеет собственный mailbox, а Go каналы разделяются между горутинами
  • Параллельные алгоритмы — Fan-out/fan-in паттерн Go - основа для реализации параллельных алгоритмов: sort, prefix sum, map-reduce

Вопросы для размышления

  • В каком сценарии мьютекс эффективнее канала - когда горутин очень много и данные маленькие?
  • Как паттерн context.WithCancel/WithTimeout через каналы реализует cooperative cancellation?
  • Почему Go выбрал CSP вместо Actor Model, если обе модели решают ту же проблему?

Связанные уроки

  • os-05-sync
CSP и каналы: Go

0

1

Войти