Ответ
Go не всегда сам переключает горутины. Планировщик (scheduler) переключает их в точках, где горутина может быть приостановлена: системные вызовы, операции с каналами, блокировки, вызовы runtime.Gosched(). Если горутина выполняет долгие вычисления без таких точек, она может занять поток надолго.
Пример:
func main() {
go func() {
for i := 0; i < 1e10; i++ {} // Долгий цикл без точек yield
}()
time.Sleep(time.Second) // Другая горутина может не успеть выполниться
}
Для явного переключения можно использовать runtime.Gosched():
for i := 0; i < 1e10; i++ {
if i%1e6 == 0 {
runtime.Gosched() // Даём шанс другим горутинам
}
}