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

В чем разница между горутиной и системным потоком?

2.0 Middle🔥 241 комментариев
#Конкурентность и горутины#Операционные системы и Linux

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

Горутины vs Системные потоки

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

Основные различия

1. Затраты памяти

  • Системный поток: ~1-8 МБ памяти на один поток
  • Горутина: ~2-4 КБ памяти на одну горутину
  • Можно запустить миллионы горутин на одной машине, потоков — только тысячи

2. Переключение контекста

  • Системный поток: тяжелое переключение (save/restore всего state CPU)
  • Горутина: легкое переключение (управляется Go runtime, сохраняются только необходимые данные)
  • Горутины переключаются в пользовательском пространстве, без вмешательства ОС

3. Планирование (scheduling)

  • Системные потоки: управляются ОС (ОС решает, когда переключить)
  • Горутины: управляются Go runtime (M:N scheduling — много горутин на несколько потоков)

Модель M:N scheduling в Go

Go использует М потоков (OS threads) для запуска N горутин:

┌─────────────────────────────────────┐
│  Программа Go (миллионы горутин)    │
│  G (Goroutine)                      │
└─────────────────────────────────────┘
          ↓ (Go Runtime)
┌─────────────────────────────────────┐
│  M (OS Threads) — обычно 4-16       │
│  П (Processors) — ядра CPU          │
└─────────────────────────────────────┘

Как работает:

  • Runtime планирует горутины на потоки
  • Одна горутина может выполняться на разных потоках
  • Если горутина блокируется на I/O, поток не замораживается — runtime создаёт новый поток

Практический пример

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup
    
    // Запускаем 10000 горутин
    for i := 1; i <= 10000; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            time.Sleep(1 * time.Second)
            fmt.Println("Goroutine", id, "done")
        }(i)
    }
    
    wg.Wait()
    fmt.Println("All goroutines completed")
}

С системными потоками это был бы кошмар — OOM. С горутинами работает отлично.

Блокирование горутин

// Если горутина блокируется на syscall (I/O):
import "syscall"

go func() {
    // Это блокирует горутину ДО завершения I/O
    // Runtime может создать новый поток для других горутин
    syscall.Read(fd, buf)
}()

GOMAXPROCS — контроль параллелизма

import "runtime"

// По умолчанию = количество ядер CPU
runtime.NumCPU()      // 8 ядер

// Ограничить потоками
runtime.GOMAXPROCS(4) // использовать максимум 4 потока

// Узнать текущее значение
fmt.Println(runtime.GOMAXPROCS(-1)) // 4

Когда что использовать?

ХарактеристикаГорутинаСистемный поток
Масштабируемость✅ Миллионы❌ Только тысячи
Память✅ 2-4 КБ❌ 1-8 МБ
Переключение✅ Быстро❌ Медленно
Простота✅ Просто❌ Сложно
Использование в Go✅ Везде❌ Редко
В чем разница между горутиной и системным потоком? | PrepBro