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

Что запускает горутину?

1.6 Junior🔥 231 комментариев
#Конкурентность и горутины#Основы Go

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

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

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

Механизм запуска горутины в Go

Горутину запускает не какой-то один конкретный механизм, а ключевое слово go, которое является частью языка Go. Когда компилятор встречает go перед вызовом функции, он создаёт новую горутину — легковесный поток выполнения, управляемый рантаймом Go (runtime), а не операционной системой напрямую.

Основные способы запуска

1. Прямой запуск с помощью go

Самый распространённый способ — использование ключевого слова перед вызовом функции:

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Привет из горутины!")
}

func main() {
    // Запускаем горутину
    go sayHello()
    
    // Даём время на выполнение горутины
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Главная горутина завершает работу")
}

2. Запуск анонимной функции

Часто используется для инлайнового выполнения:

go func() {
    fmt.Println("Анонимная горутина выполняется")
    // Выполнение какой-либо работы
}()

3. Запуск методов

Горутины можно запускать и для методов структур:

type Worker struct {
    name string
}

func (w Worker) Process() {
    fmt.Printf("Воркер %s обрабатывает данные\n", w.name)
}

func main() {
    worker := Worker{name: "Гор"}
    go worker.Process()
    time.Sleep(50 * time.Millisecond)
}

Что происходит "под капотом"?

Когда вы используете go func(), происходит следующее:

  1. Выделение стека — рантайм Go выделяет стек для новой горутины (начальный размер обычно 2-8 КБ, динамически растёт/уменьшается).

  2. Создание структуры горутины — в рантайме создаётся объект g, содержащий метаданные: состояние выполнения, стек, информацию о планировании.

  3. Постановка в очередь планировщика — горутина помещается в локальную очередь планировщика (P), который управляет выполнением горутин на потоках ОС (M).

  4. Запуск планировщиком — когда наступает очередь выполнения, планировщик Go (scheduler) назначает горутину на доступный поток ОС.

Ключевые особенности запуска

  • Независимость от главной горутины — запущенная горутина выполняется параллельно с вызвавшим её кодом (если есть доступные ядра CPU).

  • Немедленный возврат управления — оператор go не блокирует выполнение текущей горутины. Управление возвращается сразу после постановки в очередь планировщика.

  • Порядок запуска ≠ порядок выполнения — нет гарантий, когда именно начнёт выполняться горутина:

for i := 0; i < 5; i++ {
    go func(n int) {
        fmt.Printf("Горутина %d\n", n)
    }(i)
}
// Порядок вывода может быть любым!
  • Зависимость от главной горутины — если главная горутина (функция main) завершается, все остальные горутины принудительно останавливаются, даже если не закончили работу.

Практические аспекты запуска

Передача параметров

Важно правильно передавать параметры в запускаемую горутину:

// ПРАВИЛЬНО — параметр передаётся как аргумент
for i := 0; i < 3; i++ {
    go func(id int) {
        fmt.Printf("Горутина %d\n", id)
    }(i)
}

// НЕПРАВИЛЬНО — ловушка замыкания
for i := 0; i < 3; i++ {
    go func() {
        fmt.Printf("Горутина %d\n", i) // Все увидят значение 3!
    }()
}

Использование WaitGroup для синхронизации

Для ожидания завершения горутин используют sync.WaitGroup:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 5; i++ {
        wg.Add(1) // Увеличиваем счётчик
        go func(id int) {
            defer wg.Done() // Уменьшаем при завершении
            fmt.Printf("Горутина %d завершена\n", id)
        }(i)
    }
    
    wg.Wait() // Ждём завершения всех горутин
    fmt.Println("Все горутины завершены")
}

Что НЕ запускает горутину?

Важно понимать, что следующие конструкции НЕ создают новые горутины:

  • Обычный вызов функции function() — выполняется в текущей горутине
  • Вызов метода object.Method() — выполняется в текущей горутине
  • Замыкания (closures) — сами по себе не создают горутины

Заключение

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