Планировщик в Go: переключение контекста
Планировщику Go требуются четко определенные события пользовательского пространства, которые происходят в безопасных точках кода для переключения контекста. Эти события и безопасные точки проявляются в вызовах функций. Вызовы функций имеют решающее значение для работоспособности планировщика Go. Сегодня (с Go 1.11 или ниже) если вы запускаете какие-либо жесткие циклы, которые не вызывают вызовы функций, вы вызываете задержки в планировщике и сборке мусора. Крайне важно, чтобы вызовы функций происходили в разумные сроки.
Примечание. Существует предложение для версии 1.12, которое было принято для применения некооперативных методов вытеснения внутри планировщика Go, чтобы обеспечить вытеснение узких циклов.
Так как Go использует собственный планировщик, это позволяет экономить на переключениях контекста между userspace и kernel space.
В программах Go происходят четыре класса событий, которые позволяют планировщику принимать решения о планировании. Это означает не то, что это всегда будет происходить на одном из этих событий, а то, что планировщик получает возможность:
-
Использования ключевого слова go.
-
Сбора мусора.
-
Системных вызовов.
-
Синхронизации и оркестровки.
Использование ключевого слова go
Ключевое слово go — это то, как ты создаешь горутины. Как только новая горутина создана, она дает планировщику возможность принять решение о планировании
Сборщик мусора GC
Поскольку GC (garbage collector) работает с использованием собственного набора горутин, этим горутинам требуется время на M для запуска. Это приводит к тому, что сборщик мусора создает хаос в расписании. Однако планировщик очень хорошо разбирается в том, что делает Горутина, и будет использовать эту информацию для принятия разумных решений. Одним из разумных решений является переключение контекста горутины, которая хочет коснуться кучи, с теми, которые не касаются кучи во время GC. Когда GC работает, принимается множество решений по планированию.
Системные вызовы (syscalls)
Если Горутина делает системный вызов, который заставит Горутину заблокировать М, иногда планировщик способен контекстно-переключить Горутину с М и контекстно-переключить новую Горутину на тот же самый М. Однако иногда требуется новый М для продолжения выполнения Горутин, которые стоят в очереди в P. Как это работает, рассмотрим подробнее в следующем разделе.
Синхронизация и оркестровка
Если вызов атомарной операции, мьютекса или операции канала приведет к блокировке горутины, планировщик может контекстно-переключить новую горутину для запуска. Как только горутина сможет снова запуститься, ее можно повторно поставить в очередь и в конечном итоге переключить контекст обратно на M.