Всегда ли создаётся структура под созданную горутину?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Отличный вопрос, который касается внутренней механики планировщика Go (scheduler). Короткий ответ: да, всегда, но это не "структура" в смысле пользовательского struct, а внутренний объект планировщика, и он может быть переиспользован.
Давайте разберем подробно, что происходит при вызове go func(){}().
Что такое G, M и P?
Чтобы понять процесс, нужно знать три ключевые абстракции рантайма Go:
- G (Goroutine): Это и есть сама горутина. В рантайме она представлена структурой
g, которая содержит всю информацию о состоянии горутины: программный счетчик (pc), указатель стека, каналы, на которые она ждет, и т.д. - M (Machine): Представляет поток операционной системы (OS thread). Именно
Mвыполняет машинный код.Mпривязывается к ядру CPU. - P (Processor): Виртуальный "процессор" или контекст планировщика.
P— это ресурс, который требуетсяMдля исполнения кода горутиныG.Pхранит локальную очередь исполняемых горутин. КоличествоPпо умолчанию равно количеству логических ядер CPU (GOMAXPROCS).
Жизненный цикл: от go до выполнения
-
Создание дескриптора (структуры
g): Когда вы пишетеgo myFunction(), рантайм Go всегда создает или берет из пула новую структуруg, которая будет представлять эту горутину. В этот дескриптор записывается начальная информация: функция для выполнения, аргументы, статус (например,_Gdead).// Псевдокод, иллюстрирующий процесс func newgoroutine(fn func()) { // Рантайм получает или создает новый объект 'g' newg := runtime.malg(stackSize) // malg – allocate new g // Настраивает его newg.fn = fn newg.gopc = caller_pc newg.status = _Gdead // Изначально "мертва" // ... // Переводит в очередь на выполнение runqput(_g_.m.p.ptr(), newg) // Помещает в локальную очередь P } -
Помещение в очередь: Свежесозданная горутина
Gпомещается в локальную очередь исполнения (runqueue) тогоP, к которому привязан текущий потокM, выполняющий ваш код. Это происходит очень быстро, практически без блокировок. -
Планирование и выполнение: В какой-то момент планировщик Go на свободном потоке
M(или текущемM, если он освободился) возьмет эту горутинуGиз очереди своегоP, изменит ее статус (например, на_Grunning) и начнет выполнение ее функции.
Важные нюансы и оптимизации
Утверждение "всегда создается структура" абсолютно верно с точки зрения логики. Однако для производительности рантайм применяет оптимизации:
- Пулы повторного использования: Структуры
gне уничтожаются сразу после завершения работы горутины. Они помещаются в пул свободных горутин (gFree). При создании новой горутины рантайм сначала проверяет этот пул и может переиспользовать существующую, но уже неактивную, структуруg, инициализировав ее новыми значениями. Это делается для снижения нагрузки на сборщик мусора (GC) и аллокатора. - Статусы горутины: Структура
gсуществует на протяжении всего жизненного цикла горутины, но ее статус меняется:_Gdead(создана/в пуле),_Grunnable(в очереди),_Grunning(исполняется),_Gwaiting(ждет канал, мьютекс, системный вызов),_Gcopystack(стек меняет размер) и, наконец, снова_Gdeadпо завершении. - Системные вызовы: Если горутина выполняет блокирующий системный вызов (например, файловый ввод-вывод), планировщик может отсоединить текущий поток
Mот своегоP, чтобы этотPмог обслуживать другие горутины на другом потокеM. При этом структураgдля ожидающей горутины остается, ее статус становится_Gsyscall. Когда системный вызов завершится,Mпопытается вновь захватитьPи продолжить выполнение этойG.
Итог
- Да, всегда. Каждая новая горутина немедленно получает свой собственный внутренний объект-дескриптор — структуру
g. Без этого объекта планировщик не мог бы хранить ее состояние, стек, точку останова и управлять ею. - Это не пользовательская структура, а внутренняя структура рантайма (
runtime.g). - Физическая память под этот дескриптор может быть взята из пула ранее завершенных горутин для оптимизации.
- Существование этого объекта не зависит от того, выполняется ли горутина в данный момент, ждет ли она, или находится в очереди. Он существует от вызова
goдо момента, когда сборщик мусора окончательно освободит память (если она не была возвращена в пул).
Таким образом, горутины — это не "бесплатные" потоки в смысле памяти. Они гораздо легче потоков ОС (имеют маленький начальный стек, например, 2 КБ, и быстрый контекст-свитч), но каждая из них обладает своим собственным управляющим блоком данных в рантайме.