Ответ
Переключение контекста в 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 эффективно управляет тысячами горутин, не создавая при этом избыточной нагрузки на операционную систему.
Ответ 18+ 🔞
Смотри, вот эта вся тема с переключением контекста в Go — это, блядь, как цирк с конями, только вместо лошадей у нас горутины, а вместо дрессировщика — планировщик, сука.
Короче, представь: планировщик — это такой злой диспетчер на заводе. Одна работяга-горутина начинает долбить кирпичи, а он её — хоп! — в сторонку. «Постой, дура, ты своё отработала». И запускает другую. Всё это дело называется переключением контекста.
Чем это, блядь, круто?
А круто тем, что наш диспетчер — не начальник из ОС. Он наш, доморощенный, в пользовательском пространстве работает. Представь разницу: позвать мастера из ЖЭКа (системный вызов) или самому гвоздь забить. Вот так и тут — наши переключения в сотни раз быстрее, наносекунды, ёпта! Никакой волокиты с ядром.
А когда этот диспетчер начинает буянить?
Он не спит, сука, следит:
- Работяга затупил. Горутина полезла, например, файл читать или спать легла (
time.Sleep), или ждёт ответа из канала. Всё, диспетчер машет рукой: «Иди постой, пока». И запускает того, кто дело делать может. - Работяга сам душой покривил. Вызвал
runtime.Gosched()— типа «окей, я пас, пусть другие поработают». Редкий случай альтруизма в этом жестоком мире. - Время вышло. У каждой горутины есть свой кусок времени, квант (~10 мс). Отпахала свой срок — свободен! Планировщик может её снять, даже если она ещё не закончила. Это называется вытесняющее планирование, и да, оно реально существует.
Что он при этом тащит с собой?
Ну, не весь станок же переносить. Сохраняет только самое нужное, по-минимуму:
- Где остановился (указатель стека).
- Что делать дальше (счётчик команд).
- В каком состоянии инструменты (значения регистров).
Всё. Лёгкий такой чемоданчик. Поэтому и быстро.
Пример, чтобы вообще всё стало ясно
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 потоков). Операционная система даже не подозревает, какой у неё под носом цирк происходит, и не обосрётся от нагрузки. Красота!