Ответ
Переключение контекста в Go — это механизм, с помощью которого планировщик Go (scheduler) приостанавливает выполнение одной горутины и возобновляет выполнение другой. Этот процесс является ключевым для обеспечения конкурентности в Go.
Ключевые отличия от переключения на уровне ОС
Главное преимущество Go — переключение контекста происходит в пользовательском пространстве (user-space), а не на уровне ядра операционной системы. Это делает его на порядки быстрее (сотни наносекунд), так как не требует дорогостоящих системных вызовов.
Когда происходит переключение?
Планировщик Go принимает решение о переключении в следующих случаях:
- Блокирующие операции: Системные вызовы (например, чтение файла), ожидание данных из канала,
time.Sleep
. - Явный вызов: Вызов
runtime.Gosched()
добровольно уступает процессорное время другим горутинам. - Истечение кванта времени: Каждая горутина выполняется не дольше определённого кванта времени (~10 мс). После этого планировщик может передать управление другой горутине (preemptive scheduling).
Что сохраняется?
При переключении сохраняется минимально необходимый контекст горутины:
- Указатель стека (Stack Pointer): Где горутина остановила свое выполнение.
- Счётчик команд (Program Counter): Какую инструкцию выполнять следующей.
- Значения регистров: Текущее состояние регистров процессора для этой горутины.
Пример
func main() {
go func() {
fmt.Println("Горутина 1: начало")
// Добровольно уступаем процессор, чтобы другая горутина могла выполниться
runtime.Gosched()
fmt.Println("Горутина 1: продолжение")
}()
go func() {
fmt.Println("Горутина 2")
}()
time.Sleep(100 * time.Millisecond)
}
Благодаря модели M:N (M горутин на N потоков ОС), Go эффективно управляет тысячами горутин, не создавая при этом избыточной нагрузки на операционную систему.