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

Что такое легковесный поток?

1.3 Junior🔥 132 комментариев
#Конкурентность и горутины#Операционные системы и Linux

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

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

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

Что такое легковесный поток (goroutine)?

Легковесный поток, в контексте Go (Golang), — это goroutine (горутина) — абстракция для конкурентного выполнения функций, предоставляемая средой выполнения Go. Goroutine является ключевым механизмом для реализации конкурентности в Go и кардинально отличается от традиционных потоков операционной системы (OS threads).

Основные характеристики goroutine

  1. Легковесность:

    • Инициализация происходит быстро: goroutine создаются гораздо быстрее, чем потоки ОС (например, в Linux/Windows), которые требуют взаимодействия с ядром и выделения значительных ресурсов.
    • Минимальный расход памяти: начальный размер стека goroutine составляет всего 2 КБ, и он динамически изменяется (увеличивается или уменьшается) по мере необходимости, в то время как стек потока ОС обычно фиксирован и имеет размер от 1 до 8 МБ.
    • Низкие затраты на переключение контекста: переключение между goroutine выполняется в пользовательском пространстве (user-space) планировщиком Go, без дорогостоящих системных вызовов в ядро ОС.
  2. Управление планировщиком Go (Goroutine Scheduler):

    • Goroutine не управляются напрямую ОС. Вместо этого планировщик Go (часть runtime) распределяет множество goroutine по ограниченному числу потоков ОС (обычно равному количеству логических CPU).
    • Планировщик использует модель M:N, где M goroutine выполняются на N потоках ОС, что обеспечивает эффективное использование ресурсов.
    • Планировщик кооперативный и основан на определенных точках вытеснения (например, вызовы функций, операции ввода-вывода, каналы), но также включает вытеснение на основе таймеров для предотвращения "голодания".
  3. Простая модель использования:

    • Goroutine создаются с помощью ключевого слова go перед вызовом функции. Синтаксис прост и интуитивно понятен.
    • Нет необходимости вручную управлять пулами потоков, как во многих других языках.

Пример создания и использования goroutine

package main

import (
    "fmt"
    "time"
)

// Функция, выполняемая в goroutine
func printNumbers(prefix string) {
    for i := 1; i <= 3; i++ {
        fmt.Printf("%s: %d\n", prefix, i)
        time.Sleep(time.Millisecond * 100) // Имитация работы
    }
}

func main() {
    // Запуск двух goroutine
    go printNumbers("Goroutine-A")
    go printNumbers("Goroutine-B")

    // Даём время на выполнение goroutine (на практике используют sync.WaitGroup или каналы)
    time.Sleep(time.Second)
    fmt.Println("Основной поток завершён.")
}

Сравнение goroutine с потоками ОС и корутинами

АспектGoroutine (Go)Поток ОСКорутина (Coroutine, например в C++)
УправлениеПланировщик Go (user-space)Ядро ОСПрограммист / библиотека (user-space)
Память (стек)Динамический, от 2 КБФиксированный, 1-8 МБЧасто фиксированный, небольшой
Создание/переключениеОчень быстро, дешевоМедленно, дорогоБыстро, дешево
Параллелизм vs КонкурентностьКонкурентность, возможен параллелизм на многопроцессорных системахМожет быть и тем, и другимОбычно только конкурентность
ВзаимодействиеКаналы (channels), sync примитивыПримитивы ОС (мьютексы, семафоры)Часто ad-hoc механизмы

Ключевые преимущества goroutine

  • Высокая плотность: можно создавать сотни тысяч или даже миллионы goroutine в одной программе, что практически невозможно с потоками ОС из-за ограничений памяти и производительности.
  • Упрощение конкурентного кода: goroutine в сочетании с каналами (channels) реализуют модель CSP (Communicating Sequential Processes), что минимизирует необходимость в явных блокировках и снижает риск race conditions.
  • Эффективное использование CPU: планировщик Go распределяет goroutine по ядрам CPU, обеспечивая реальный параллелизм на многопроцессорных системах.

Под капотом: как работает планировщик Go

Планировщик Go имеет три основные сущности:

  • G (Goroutine): представляет собой саму goroutine.
  • M (Machine): представляет поток ОС (OS thread).
  • P (Processor): представляет ресурс, необходимый для выполнения Go-кода (контекст планировщика). P связывает M и G.
// Пример, демонстрирующий масштабируемость goroutine
package main

import (
    "fmt"
    "runtime"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    numGoroutines := 100000

    fmt.Printf("Запускаем %d goroutine...\n", numGoroutines)
    for i := 0; i < numGoroutines; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            // Некоторая полезная работа
            _ = id * 2
        }(i)
    }

    wg.Wait()
    fmt.Println("Все goroutine завершены.")
    fmt.Printf("Число logical CPU: %d\n", runtime.NumCPU())
}

Заключение

Легковесный поток (goroutine) — это фундаментальная строительная единица конкурентности в Go, обеспечивающая эффективное выполнение множества задач одновременно без накладных расходов традиционных потоков. Благодаря тесной интеграции с планировщиком runtime, goroutine позволяют писать высокопроизводительные, масштабируемые и при этом относительно простые конкурентные программы. Именно goroutine, в сочетании с каналами, делают Go языком, где конкурентность является не дополнительной сложностью, а естественной частью разработки.

Что такое легковесный поток? | PrepBro