Что такое горутина
Краткий ответ
Горутина — это легковесный поток выполнения (lightweight thread), управляемый рантаймом Go, а не операционной системой. Она позволяет выполнять код конкурентно. Создаётся с помощью ключевого слова go, имеет очень маленький начальный стек и может масштабироваться до сотен тысяч экземпляров в одном процессе.
go func() {
fmt.Println("hello")
}()
Подробно
Горутина — одна из ключевых концепций Go для организации конкурентности (concurrency).
В отличие от обычных OS threads:
- создаётся очень быстро;
- занимает мало памяти;
- планируется (scheduling) самим рантаймом Go;
- количество горутин может значительно превышать количество системных потоков.
Когда мы пишем:
go doWork()
рантайм создаёт новую горутину и помещает её в очередь на выполнение.
Как работает под капотом
В Go используется модель планировщика G-M-P:
- G (Goroutine) — сама горутина;
- M (Machine) — системный поток (OS thread);
- P (Processor) — логический процессор рантайма.
Рантайм распределяет горутины между потоками и процессорами.
Поэтому если у нас:
for i := 0; i < 100000; i++ {
go worker()
}
не будет создано 100000 системных потоков.
Будет создано 100000 горутин, которые небольшой группой потоков будут выполняться по мере необходимости.
Размер стека
У системного потока обычно стек измеряется мегабайтами.
У горутины стек стартует очень маленьким (порядка нескольких килобайт) и динамически растёт при необходимости.
Это одна из причин, почему можно создавать огромное количество горутин.
Конкурентность и параллелизм
Конкурентность (Concurrency)
Задачи могут выполняться независимо друг от друга.
Например:
go downloadFile()
go sendEmail()
Обе задачи запущены одновременно с точки зрения программы.
Параллелизм (Parallelism)
Задачи реально выполняются одновременно на разных ядрах процессора.
Количество одновременно работающих потоков зависит от:
runtime.GOMAXPROCS()
По умолчанию равно количеству CPU.
Когда горутина переключается
Раньше переключение происходило в основном в точках блокировки:
- системные вызовы;
- операции с каналами;
- mutex;
- sleep;
- сетевые операции.
Современный Go поддерживает также асинхронное вытеснение (asynchronous preemption), поэтому даже бесконечный цикл может быть прерван планировщиком.
Например:
for {
// долгие вычисления
}
раньше мог блокировать выполнение других горутин, сейчас рантайм умеет его вытеснять.
Что происходит при блокировке
Если горутина делает:
data := <-ch
и данных нет, она блокируется.
Поток при этом не простаивает:
- горутина переводится в состояние ожидания;
- поток может выполнять другие горутины.
Это важное отличие от модели "один поток — одна задача".
Утечки горутин (Goroutine leaks)
Можно случайно создать горутину, которая никогда не завершится:
func worker(ch chan int) {
<-ch
}
func main() {
go worker(make(chan int))
}
Здесь канал никто не заполнит.
Горутина будет висеть в памяти до завершения процесса.
В продакшене это приводит к:
- росту памяти;
- накоплению заблокированных горутин;
- деградации производительности.
Поэтому важно:
- использовать
context.Context; - закрывать каналы;
- корректно завершать воркеры.
Что часто спрашивают следом
Может ли горутина выполняться на разных потоках?
Да.
Планировщик может переносить горутину между различными OS threads.
Гарантирует ли go func() немедленный запуск?
Нет.
Она только ставится в очередь планировщика.
Можно ли узнать ID горутины?
Официального API нет.
Это сделано специально, чтобы разработчики не строили логику вокруг идентификаторов горутин.
Является ли горутина аналогом async/await?
Не совсем.
В Go используется модель CSP (Communicating Sequential Processes):
- запускаем конкурентные задачи через горутины;
- взаимодействуем через каналы и синхронизацию.
Это другая модель по сравнению с async/await в JavaScript, C# или Kotlin.
Ответ уровня Middle+
Горутина — это легковесная единица выполнения, управляемая рантаймом Go. Она значительно дешевле системного потока, имеет динамически растущий стек и планируется через модель G-M-P. Благодаря этому приложение может эффективно запускать сотни тысяч конкурентных задач. Для взаимодействия между горутинами обычно используются каналы, mutex'ы и другие примитивы синхронизации. Важно следить за корректным завершением горутин, чтобы не допускать goroutine leaks.
