Вопрос проверяет понимание жизненного цикла горутин в Go, что важно для собеседований на позиции разработчика на Go.
Горутина — это легковесный поток выполнения в Go, управляемый планировщиком языка. В отличие от потоков ОС, горутины не имеют фиксированного стека и могут динамически расти, что позволяет создавать тысячи горутин без значительных накладных расходов. Понимание состояний горутины помогает разработчику предсказывать поведение конкурентных программ и избегать проблем, таких как гонки данных или взаимные блокировки.
Планировщик Go использует модель M:N, где M — потоки ОС, а N — горутины. Когда горутина выполняет блокирующую операцию (например, ожидание канала), она переходит в состояние waiting, а планировщик запускает другую горутину из очереди runnable. После завершения ожидания горутина становится runnable и снова попадает в очередь.
package main
import (
"fmt"
"time"
)
func worker(id int, ch chan int) {
fmt.Printf("Горутина %d: старт\n", id)
time.Sleep(time.Second) // Переход в waiting
ch <- id // Отправка в канал
fmt.Printf("Горутина %d: завершение\n", id)
}
func main() {
ch := make(chan int)
go worker(1, ch) // Горутина создана, становится runnable
go worker(2, ch)
<-ch // main ожидает данные, переходит в waiting
<-ch
fmt.Println("Все горутины завершены")
}В этом примере горутины worker сначала выполняются (running), затем при вызове time.Sleep переходят в waiting, после чего планировщик может запустить другую горутину. Когда sleep завершается, горутина становится runnable и затем снова running.
Знание состояний помогает при отладке конкурентных программ: если программа зависает, можно использовать профилировщик (например, pprof) для анализа, в каком состоянии находятся горутины. Это позволяет выявить взаимные блокировки (deadlock), когда все горутины находятся в waiting и никто не может продолжить выполнение.
Итог: Понимание состояний горутины — базовая концепция для написания эффективных и безопасных конкурентных программ на Go. Это знание необходимо для диагностики проблем производительности и корректности, а также для осознанного использования примитивов синхронизации, таких как каналы и мьютексы.