Ответ
Строго говоря, в Go существует только один вид горутин. Горутина — это легковесный поток исполнения, управляемый планировщиком Go (Go scheduler), а не операционной системой. Все горутины создаются и работают одинаково с точки зрения рантайма.
Однако на практике горутины можно классифицировать по их роли, состоянию или жизненному циклу в приложении. Это помогает лучше понимать архитектуру конкурентной программы.
Классификация по роли и состоянию:
-
Основная горутина (Main goroutine)
- Это горутина, в которой выполняется функция
main(). Она запускается автоматически при старте программы. - Её завершение приводит к немедленному завершению всей программы, даже если другие горутины еще не закончили свою работу.
- Это горутина, в которой выполняется функция
-
Рабочие горутины (Worker goroutines)
- Это наиболее распространенный тип. Они запускаются с помощью ключевого слова
goдля выполнения конкретной задачи конкурентно (например, обработка HTTP-запроса, вычисление данных, работа с файлами).func main() { var wg sync.WaitGroup wg.Add(1) // Запуск рабочей горутины go func() { defer wg.Done() fmt.Println("Я — рабочая горутина") }() wg.Wait() // Ожидаем завершения }
- Это наиболее распространенный тип. Они запускаются с помощью ключевого слова
-
Горутины в состоянии ожидания (Blocking goroutines)
- Горутины, которые заблокированы в ожидании какого-либо события: I/O операции (сеть, диск), чтение из канала, ожидание мьютекса или таймера.
- Планировщик Go эффективно управляет ими, не блокируя поток ОС, а переключая на выполнение другую готовую горутину.
-
"Утёкшие" горутины (Leaked goroutines)
- Это горутины, которые никогда не завершат свое выполнение из-за ошибки в логике. Они продолжают существовать и потреблять память до конца работы программы.
- Частая причина утечки: горутина блокируется при отправке или чтении из канала, из которого никто никогда не прочитает или в который никто никогда не запишет.
// Пример утечки: горутина будет вечно ждать, // так как из канала ch никто не читает. ch := make(chan int) go func() { ch <- 1 // Блокировка навсегда }() // time.Sleep(time.Second) // Если раскомментировать, программа завершится, а горутина утечет
Таким образом, хотя технически все горутины одинаковы, понимание их ролей и состояний критически важно для написания корректных и эффективных конкурентных программ на Go.
Ответ 18+ 🔞
А, ну ты про горутины в Go спрашиваешь! Так, слушай, сейчас разложу по полочкам, как есть, без этой академической пыли в глазах.
Вот смотри, с технической-то точки зрения, горутина она и в Африке горутина. Одна хуйня. Это типа легковесный такой поток, которым не система вертит, а наш родной Go-шный планировщик. Все они создаются одинаково, живут одинаково. Но вот людишки, блядь, любят всё по полочкам раскладывать, по ролям и состояниям. И, честно говоря, это охуенно помогает не выстрелить себе в ногу.
Так, поехали, какие они бывают в жизни.
1. Главная, мамкина горутина (Main goroutine)
Это та, с которой всё начинается. Там, где main() выполняется. Запустилась — и понеслась. И вот тут главный прикол, ёпта: как только эта горутина кончает — вся программа накрывается медным тазом. Всё. Остальные, даже если они в самом разгаре, получают пизды и уходят в небытие. Жестко, но так устроено.
2. Рабочие лошадки (Worker goroutines)
Самые распространённые. Ты их плодишь ключевым словом go, чтобы какую-то хуйню делать параллельно. Запрос обработать, файл прочитать, цифры посчитать.
func main() {
var wg sync.WaitGroup
wg.Add(1)
// Запуск рабочей горутины
go func() {
defer wg.Done()
fmt.Println("Я — рабочая горутина")
}()
wg.Wait() // Сидим, ждём, пока пацан отработает
}
3. Горутины в отключке (Blocking goroutines)
Эти сидят и ждут. Ждут чего угодно: пока данные по сети придут, пока в канал что-то запишут, пока мьютекс отпустят, пока таймер дзынькнет. Весь кайф в том, что пока они ждут, планировщик не тупит, а переключается на других — эффективно, блядь.
4. Утёкшие, блядь, горутины (Leaked goroutines)
А вот это, сука, главная головная боль! Это те, которые никогда не завершатся. Они как зомби — сидят в памяти, жрут ресурсы, пока программа не умрёт. Откуда берутся? Да чаще всего из-за кривых каналов! Вот смотри, классика жанра:
// Пример утечки: горутина будет вечно ждать,
// так как из канала ch никто не читает.
ch := make(chan int)
go func() {
ch <- 1 // И всё, приехали. Тут она и повиснет навеки.
}()
// time.Sleep(time.Second) // Допустим, программа тут закончится, а эта бедолага так и будет висеть в памяти, как призрак.
Вот это и есть та самая «утечка». Горутина создана, а пути назад для неё нет. Пиздец и точка.
Короче, хоть все горутины под капотом — одно и то же, понимать, кто из них кто в твоей программе, — это залог того, что ты не наделаешь асинхронных граблей, на которые потом сам же и наступишь.