← Назад к вопросам
В чем разница между горутиной и системным потоком?
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 | ✅ Везде | ❌ Редко |