Опишите модель многозадачности в Go и как работает его планировщик.

Ответ

В Go используется гибридная модель многозадачности, сочетающая кооперативную и вытесняющую многозадачность, реализованная на основе модели M:N.

  • Модель M:N: Рантайм Go распределяет M горутин (goroutines) для выполнения на N потоках операционной системы (OS threads). Обычно N равно количеству ядер CPU.

Планировщик Go управляет горутинами, используя два подхода:

  1. Кооперативная многозадачность (Cooperative Multitasking):

    • Горутина добровольно уступает процессорное время другой горутине.
    • Это происходит в "безопасных точках": при блокирующих системных вызовах (I/O), операциях с каналами, мьютексами или при явном вызове runtime.Gosched().
  2. Вытесняющая многозадачность (Preemptive Multitasking):

    • Начиная с Go 1.14, планировщик может принудительно прервать горутину, которая выполняется слишком долго без добровольной уступки (например, в долгих циклах без блокировок).
    • Это решает проблему, когда одна вычислительно-интенсивная горутина могла монополизировать поток ОС, вызывая "голодание" других горутин.
    • Механизм основан на асинхронных сигналах, которые рантайм посылает потоку, чтобы проверить состояние горутин и при необходимости переключить контекст.
func main() {
    // Эта горутина будет вытеснена планировщиком, 
    // если будет работать слишком долго без блокировок.
    go func() {
        for { 
            // Длительные вычисления
        }
    }()

    // Эта горутина добровольно уступит управление.
    go func() {
        fmt.Println("Уступаю очередь...")
        runtime.Gosched()
    }()

    // ...
}