Как реализован параллелизм в Go
Что проверяют
Различаете ли вы конкурентность и параллелизм и знаете ли, чем ограничен второй.
Как отвечать
Начните с различия, оно здесь ключевое. Конкурентность это способ структурировать программу как набор независимо выполняющихся задач. Параллелизм это фактическое одновременное выполнение на нескольких ядрах. Go даёт конкурентность как модель языка, а параллелизм получается сам, если ядер больше одного.
Средства:
- горутины: запуск задачи словом
go. Дёшевы, стек начинается с пары килобайт и растёт по надобности, поэтому их спокойно заводят десятками тысяч; - каналы для передачи данных и синхронизации,
selectдля работы с несколькими каналами и таймаутами; sync: группа ожидания, мьютексы, однократное выполнение;sync/atomicдля счётчиков и флагов;contextдля отмены и ограничения времени.
Исполнением управляет планировщик по схеме многие ко многим: горутины распределяются по логическим процессорам, число которых задаёт GOMAXPROCS и по умолчанию равно числу ядер. Простаивающий процессор крадёт половину задач у соседа, поэтому нагрузка балансируется без центрального диспетчера. Блокирующий системный вызов не останавливает остальных: рантайм передаёт контекст другому потоку, а сетевые операции вообще уходят в опрашиватель и не занимают поток.
Про философию: девиз «не общайтесь разделением памяти, разделяйте память общением» означает предпочтение каналов. Но это не догма: для защиты небольшого общего состояния мьютекс проще и быстрее канала, и стандартная библиотека сама так делает.
Практическая оговорка про контейнеры: GOMAXPROCS по умолчанию видит число ядер узла, а не лимит контейнера. При жёстком ограничении процессора это приводит к лишним переключениям, и значение задают явно.
Проверять корректность обязательно детектором гонок: go test -race.
Где спотыкаются
Считают конкурентность и параллелизм синонимами.
Думают, что GOMAXPROCS ограничивает число горутин.
Используют каналы там, где достаточно мьютекса, и усложняют код.
Не запускают тесты с детектором гонок.