Конкуренция в Go
Теория: Инструменты и отладка
Конкурентные программы ведут себя иначе, чем последовательные. Ошибки здесь редко проявляются явно: гонки данных случаются только при определенном межпотоковом расписании, блокировки появляются не всегда, а потерянные миллисекунды на ожидании легко спрятаны в общей картине работы. Именно поэтому такой код всегда сопровождают специализированными инструментами.
Go предлагает три ключевых механизма, которые помогают увидеть внутреннее устройство программы: детектор гонок -race, профилировщик pprof и настройку параллелизма через GOMAXPROCS. Каждый из них решает свою задачу: один показывает проблемы согласованности доступа, второй — узкие места по времени и памяти, третий — управление количеством используемых процессорных ядер.
Детектор гонок: go run -race
Гонка данных возникает, когда несколько горутин одновременно читают или пишут одну и ту же переменную без синхронизации. Итоговое значение зависит от того, какая операция выполнилась первой. Такие ошибки не видны глазами и почти не воспроизводятся по требованию.
Команда:
или
запускает программу в режиме слежения за доступом к памяти. Рантайм автоматически отслеживает все чтения и записи и сообщает, если две операции конфликтуют.
Минимальный пример:
Запуск с -race покажет стек вызовов, укажет строку и место конфликта.
После исправления — например, через sync.Mutex() — предупреждения исчезнут.
Важно помнить: режим -race замедляет выполнение и увеличивает память в несколько раз. Его используют только в тестах и отладке, а не в продакшене.
Профилировщик pprof
Когда программа работает корректно, но медленно, помогает профилирование. Оно показывает, как распределяется процессорное время, где возникают задержки, сколько памяти выделяет код и насколько часто он блокируется на мьютексах или каналах.
Профиль из тестов
Для коротких программ:
В отчете будут видны функции, в которых тратится больше всего времени.
Профиль работающего приложения
В сервис pprof включают через HTTP:
Снятие профиля:
Результат — отчет или flame graph (через -http=:8080), где видно, какие функции нагружают CPU, сколько выделено памяти, где программа ждет замок или канал.
Пример функции, потребляющей CPU:
Профиль честно показывает, что почти все время уходит в math.Sqrt, и это не ошибка — просто тяжелая операция.
Параллелизм и GOMAXPROCS
Количество горутин в Go не эквивалентно количеству ядер. Параллельно может выполняться только столько горутин, сколько логических ядер Go разрешено использовать. Это значение задает GOMAXPROCS.
По умолчанию оно равно числу CPU, но его можно изменить:
или:
Пример, показывающий влияние параметра:
Если GOMAXPROCS равен 1, все воркеры делят одно ядро. При увеличении до 2, 4 и выше выполнение ускоряется — но только до предела количества физических ядер. Дальнейшее увеличение уже не помогает: возрастает только число переключений контекста.
Общая стратегия отладки
Инструменты лучше всего работают в связке. Стандартная последовательность выглядит так:
- Проверить корректность. Запустить программу с
-raceи устранить все гонки данных. - Понять производительность. С помощью
pprofизмерить, куда уходят CPU, память и время ожидания. - Настроить параллелизм. Подобрать
GOMAXPROCSи параметры воркеров так, чтобы эффективно использовать ресурсы системы.
После этих шагов конкурентная программа становится наблюдаемой: ее поведение можно измерить


