Какие существуют виды или классификации горутин в Go?

Ответ

Строго говоря, в Go существует только один вид горутин. Горутина — это легковесный поток исполнения, управляемый планировщиком Go (Go scheduler), а не операционной системой. Все горутины создаются и работают одинаково с точки зрения рантайма.

Однако на практике горутины можно классифицировать по их роли, состоянию или жизненному циклу в приложении. Это помогает лучше понимать архитектуру конкурентной программы.

Классификация по роли и состоянию:

  1. Основная горутина (Main goroutine)

    • Это горутина, в которой выполняется функция main(). Она запускается автоматически при старте программы.
    • Её завершение приводит к немедленному завершению всей программы, даже если другие горутины еще не закончили свою работу.
  2. Рабочие горутины (Worker goroutines)

    • Это наиболее распространенный тип. Они запускаются с помощью ключевого слова go для выполнения конкретной задачи конкурентно (например, обработка HTTP-запроса, вычисление данных, работа с файлами).
      func main() {
      var wg sync.WaitGroup
      wg.Add(1)
      // Запуск рабочей горутины
      go func() {
          defer wg.Done()
          fmt.Println("Я — рабочая горутина")
      }()
      wg.Wait() // Ожидаем завершения
      }
  3. Горутины в состоянии ожидания (Blocking goroutines)

    • Горутины, которые заблокированы в ожидании какого-либо события: I/O операции (сеть, диск), чтение из канала, ожидание мьютекса или таймера.
    • Планировщик Go эффективно управляет ими, не блокируя поток ОС, а переключая на выполнение другую готовую горутину.
  4. "Утёкшие" горутины (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) // Допустим, программа тут закончится, а эта бедолага так и будет висеть в памяти, как призрак.

Вот это и есть та самая «утечка». Горутина создана, а пути назад для неё нет. Пиздец и точка.

Короче, хоть все горутины под капотом — одно и то же, понимать, кто из них кто в твоей программе, — это залог того, что ты не наделаешь асинхронных граблей, на которые потом сам же и наступишь.