Может ли планировщик переключить goroutine при длительном выполнении?
Что проверяют
Знаете ли вы, что планировщик Go давно не чисто кооперативный, и видите ли разницу между вытеснением, блокирующим системным вызовом и ожиданием на канале. Следом часто просят объяснить, почему пустой бесконечный цикл раньше мог повесить программу.
Как отвечать
Да, может. Горутина уступает процессор в трёх ситуациях:
- сама: блокировка на канале,
sync.Mutex,time.Sleep, сетевой ввод-вывод. Сеть идёт через netpoller: горутина паркуется, поток ОС не занят; - системный вызов: при долгом блокирующем вызове (чтение файла, cgo) поток M уходит в ядро, а монитор
sysmonотбирает у него P и отдаёт другому потоку, чтобы остальные горутины продолжали работать; - асинхронное вытеснение: с Go 1.14
sysmonзамечает горутину, которая работает дольше примерно 10 мс, и посылает потоку сигнал (SIGURGв Unix). Горутина останавливается в безопасной точке и возвращается в очередь.
До 1.14 точки вытеснения были только в прологе функций, где проверяется стек. Цикл вида for { i++ } без вызовов функций не прерывался: при GOMAXPROCS=1 остальные горутины голодали, а сборщик мусора не мог начать stop-the-world, и вставала вся программа.
Модель стоит назвать через G, M, P: горутины, потоки ОС и логические процессоры, которых по умолчанию столько же, сколько доступных ядер (GOMAXPROCS). У каждого P своя локальная очередь, есть глобальная и перехват работы у соседей. runtime.Gosched() уступает процессор явно, но в современном коде нужен редко. Отключить асинхронное вытеснение можно через GODEBUG=asyncpreemptoff=1, это инструмент отладки, а не настройка для прода.
Где спотыкаются
Отвечают по старым статьям, что планировщик кооперативный и горутина крутится, пока сама не уступит. Для текущих версий это неверно.
Путают вытеснение с системным вызовом. Во втором случае горутину не прерывают, она продолжает ждать ядро, просто её P переходит к другому потоку.
Считают, что вытеснение даёт справедливость и приоритеты. Приоритетов у горутин нет, а тяжёлые CPU-задачи всё равно стоит ограничивать пулом воркеров или семафором, иначе растут задержки у остальных.