← Назад к вопросам

Где порождаются горутины?

2.0 Middle🔥 181 комментариев
#Конкурентность и горутины

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Место порождения горутин в Go

Горутины — это легковесные потоки выполнения, являющиеся фундаментальной концепцией параллелизма в Go. Они порождаются не в одном фиксированном месте, а в любой точке программы, где вызывается ключевое слово go, предваряющее вызов функции или метода.

Ключевой механизм: оператор go

Порождать горутины можно везде, где есть выполняемый код: в функции main(), в других функциях, в методах структур, внутри уже запущенных горутин и даже в анонимных функциях. Единственное требование — наличие вызова функции с префиксом go.

package main

import (
    "fmt"
    "time"
)

func main() {
    // Порождаем горутину из main, вызывая именованную функцию
    go printHello()

    // Порождаем горутину из main, используя анонимную функцию
    go func() {
        fmt.Println("Анонимная горутина")
    }()

    // Порождаем горутину внутри другой горутины
    go func() {
        go nestedGoroutine()
    }()

    // Даем время на выполнение (на практике используют sync.WaitGroup или каналы)
    time.Sleep(100 * time.Millisecond)
}

func printHello() {
    fmt.Println("Hello from goroutine")
}

func nestedGoroutine() {
    fmt.Println("Вложенная горутина")
}

Где конкретно порождаются горутины "под капотом"?

Хотя синтаксически горутина создается оператором go, её реальное создание и планирование происходит в рантайме Go (runtime). Процесс включает несколько слоёв:

  1. Пользовательский код: Вы пишете go someFunction().
  2. Вызов в рантайме: Компилятор Go преобразует это в вызов внутренней функции рантайма, например, runtime.newproc(). Эта функция отвечает за создание новой горутины.
  3. Создание структуры g: Рантайм выделяет память (обычно из пула) для структуры g, которая представляет собой дескриптор горутины. В неё записывается начальная информация: указатель на функцию для выполнения, аргументы, статус, стек и др.
  4. Помещение в очередь: Новосозданная горутина помещается в очередь исполнения (runqueue) локального пула потоков (P) текущей машины (M) или в глобальную очередь. Планировщик Go (scheduler) в нужный момент выделит ей поток операционной системы для выполнения.
// Упрощенное представление того, что происходит на уровне рантайма
// (это не реальный код, а иллюстрация)

// 1. Ваш код:
go myFunc(42)

// 2. Преобразуется компилятором примерно в:
runtime.newproc(func() { myFunc(42) })

Практические аспекты и места порождения

  • Функция main: Это точка входа программы. При запуске приложения сама функция main выполняется в специальной главной горутине (main goroutine).
  • Инициализация пакетов: Горутины не могут быть порождены на этапе инициализации пакетов (в init()-функциях или при инициализации глобальных переменных), так как планировщик ещё не запущен.
  • Системные вызовы и ввод-вывод: Горутины часто порождаются автоматически внутри стандартной библиотеки при работе с сетью, файлами или таймерами. Например, при вызове http.ListenAndServe(), для каждого нового соединения в фоне порождается горутина.
  • Паттерны параллелизма:
    *   **Worker Pool**: Горутины-воркеры порождаются заранее и ожидают задач из канала.
```go
// Пример порождения воркеров
jobs := make(chan int)
for w := 1; w <= 3; w++ {
    go worker(w, jobs) // Порождение 3 горутин
}
```
    *   **Fan-out**: Одна горутина-производитель порождает несколько горутин-обработчиков.
    *   **Обработка запросов**: В веб-сервере каждая горутина порождается для обработки отдельного HTTP-запроса.

Важные замечания

  • Планировщик, а не ОС: Горутины — это не потоки ОС. Они планируются рантаймом Go, а не ядром операционной системы. Это делает их создание и переключение крайне дешёвыми (начальный размер стека ~2 КБ против ~1-2 МБ у потока ОС).
  • Отсутствие гарантий порядка: Порождение горутины оператором go не гарантирует немедленного начала её выполнения. Планировщик запустит её, когда будут доступны ресурсы. Порядок запуска непредсказуем.
  • Координация: Поскольку горутины выполняются конкурентно, для синхронизации и обмена данными обязательно нужно использовать примитивы: каналы (channels), мьютексы (sync.Mutex), группы ожидания (sync.WaitGroup) и др.

Итог: Горутины порождаются синтаксически в любом месте кода с помощью оператора go, а физически — в памяти программы силами рантайма Go, который выделяет для них структуры данных и ставит в очередь на выполнение планировщиком. Это мощная абстракция, позволяющая писать высокопроизводительные конкурентные программы с относительно простым кодом.

Где порождаются горутины? | PrepBro