Горутины и потоки ОС — это принципиально разные механизмы параллельного выполнения кода. Рассмотрим их ключевые различия в контексте Go.
Характеристика | Горутина | Поток ОС |
---|---|---|
Реализация | Уровень языка (runtime Go) | Уровень ОС |
Память | 2-8 КБ стек (динамический) | 1-2 МБ (фиксированный) |
Переключение контекста | 100-200 нс |
1000-1500 нс |
Планирование | Кооперативное (с вытеснением с Go 1.14) | Вытесняющее |
Модель | M:N (много горутин на few потоках) | 1:1 |
Создание | Дешевое (go func() ) |
Дорогое |
// Пример создания 10000 горутин
func main() {
for i := 0; i < 10000; i++ {
go func(n int) {
fmt.Println(n)
}(i)
}
time.Sleep(time.Second)
}
// Пример переключения горутин
func worker() {
for {
runtime.Gosched() // Явное переключение
}
}
Горутины используют более легковесные механизмы:
// Пример синхронизации горутин
ch := make(chan int)
go func() { ch <- 42 }()
value := <-ch
Горутины работают в модели M:N:
[Горутины (G)] ---исполняются на---> [Процессоры (P)] ---мапятся на---> [Потоки ОС (M)]
Горутины идеальны для:
Потоки нужны для:
func handleConn(conn net.Conn) {
defer conn.Close()
// Обработка соединения
}
func main() {
ln, _ := net.Listen("tcp", ":8080")
for {
conn, _ := ln.Accept()
go handleConn(conn) // Новая горутина на соединение
}
}
var wg sync.WaitGroup
func worker(id int) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i)
}
wg.Wait()
}
Ключевые отличия горутин от потоков:
Горутины — это мощная абстракция Go для конкурентности, которая использует потоки ОС под капотом, но предоставляет более удобный и эффективный интерфейс.