Вопрос проверяет понимание работы планировщика Go (Goroutine Scheduler) и различий между кооперативной и вытесняющей многозадачностью.
Go использует вытесняющую многозадачность на уровне потоков ОС, но кооперативную внутри самих горутин. Планировщик Go (GMP-модель) распределяет горутины по потокам (M), переключая их при блокировках (I/O, каналы) или через вызов runtime.Gosched().
Кооперативная (Cooperative):
Горутина сама передаёт управление (например, при блокировке).
Плюс: меньше накладных расходов.
Минус: если горутина не отдаст управление, другие "зависнут".
Вытесняющая (Preemptive):
ОС принудительно переключает потоки.
В Go это работает на уровне потоков (M), но не горутин.
G (Goroutine) – легковесный поток.
M (Machine) – поток ОС (1 M = 1 ядро CPU).
P (Processor) – контекст выполнения (управляет очередью горутин).
При блокирующих операциях (каналы, системные вызовы).
При вызове runtime.Gosched().
Каждые ~10 мс (планировщик проверяет, не пора ли переключиться).
Пример:
func main() {
go func() {
for {
runtime.Gosched() // Явно передаём управление
}
}()
}Вывод:
Go сочетает эффективность кооперативной модели с надёжностью вытеснения на уровне потоков.