Конкурентность в Go

Программы, процессы, потоки, горутины и планировщик G-M-P — простыми словами

🗂 Что такое Программа?

Программа — это набор инструкций, написанных на языке программирования, которые говорят компьютеру, что делать. Это пассивный объект.

Рецепт борща в кулинарной книге. Он просто лежит на странице и описывает шаги — сам по себе ничего не готовит.

В компьютере это ваш скомпилированный файл (например, myapp.exe в Windows или myapp в Linux). Он лежит на диске и ждёт запуска.

⚙️ Что такое Процесс?

Процесс — это экземпляр выполняющейся программы. Когда вы запускаете программу, ОС создаёт для неё процесс. Процесс — это активный объект.

Вы (повар) открыли книгу и начали готовить борщ. Взяли кастрюлю, налили воду, поставили на плиту. Этот активный процесс готовки — и есть процесс.
Изолированность

Если в вашем процессе готовится борщ, это никак не влияет на соседний процесс, где пекут пирог. У каждого своя «кухня».

Свои ресурсы

ОС выделяет каждому процессу свою память, файлы и другие ресурсы. Процессы не могут напрямую использовать память друг друга.

🧵 Что такое Поток (Thread)?

Поток — это наименьшая единица выполнения кода внутри процесса. Один процесс может иметь один или несколько потоков.

Вы готовите борщ в одиночку — это один поток. Вы последовательно нарезаете овощи, варите, помешиваете. Если суп нужно варить 30 минут — вы просто ждёте.

Если нанять помощника — на кухне появятся два потока. Один следит за супом, другой в это же время готовит салат. Они пользуются общими инструментами, что создаёт риск конфликтов — race condition.

Легковесность

Создать поток («нанять помощника») гораздо дешевле, чем создавать новый процесс («строить новую кухню»).

Общие ресурсы

Все потоки внутри одного процесса имеют доступ к общей памяти. Это удобно для обмена данными, но создаёт риски.

🚧 Проблема однопоточной программы

Представьте, что вы — единственный работник на почте. Пока вы ждёте ответа сервера на запрос, вы ничего не делаете. Клиенты в очереди стоят и ждут. Программа «зависла».

Повар, который поставил суп вариться на 30 минут и просто стоит, смотрит на кастрюлю — вместо того чтобы нарезать салат.

Именно эту проблему решает конкурентность в Go — возможность переключаться на другие задачи, пока одна из них ожидает.


☕ Конкурентность vs Параллелизм

Конкурентность (Concurrency)

Deal with multiple things at once

Один бариста, но очень опытный. Ставит эспрессо вариться → переключается на американо → возвращается к капучино. В каждый момент он делает только одно, но умно жонглирует задачами.

Цель: отзывчивость программы. Работает и на одном ядре.

Параллелизм (Parallelism)

Do multiple things at once

Два бариста работают физически одновременно: один делает капучино, другой — американо. Задачи выполняются в одно и то же время.

Цель: скорость вычислений. Требует нескольких ядер CPU.

КритерийКонкурентностьПараллелизм
СутьСтруктура кодаСпособ выполнения на железе
Аналогия1 бариста, multitasking2 бариста одновременно
ЖелезоДостаточно 1 ядраНужно несколько ядер
ЦельОтзывчивость, правильная структураСкорость, производительность
«Не общайтесь, разделяя память — разделяйте память, общаясь.»
— философия конкурентности в Go

🏗 Планировщик Go: модель G-M-P

Go не доверяет ОС управлять горутинами напрямую. Вместо этого у него есть собственный эффективный планировщик.

G
Goroutine — Задача
«Карточка с заданием». Создаётся очень дёшево. Содержит код, который нужно выполнить.
M
Machine — Рабочий
Настоящий поток ОС. «Мощный», но «дорогой». Физически выполняет код. Go держит их пул.
P
Processor — Рабочее место
Связывает G и M. Имеет локальную очередь горутин. По умолчанию — 1 P на каждое ядро CPU.

Как это работает вместе

Почему это хорошо?

🚀Переключение между горутинами происходит в пространстве пользователя — в разы быстрее, чем между потоками ОС.
📦Можно создавать сотни тысяч горутин — они дёшевы, как карточки.
🔧Автоматически использует все ядра CPU через балансировку Work Stealing.
С Go 1.5 GOMAXPROCS по умолчанию равен числу ядер — ничего настраивать не нужно.