← Назад к вопросам

В чем разница между кооперативной и вытесняющей многозадачностью?

3.0 Senior🔥 141 комментариев
#Конкурентность и горутины#Операционные системы и Linux

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между кооперативной и вытесняющей многозадачностью

Кооперативная многозадачность и вытесняющая многозадачность — это две фундаментальные модели управления выполнением процессов или потоков (threads) в операционных системах и runtime-средах, таких как Go. Их основное отличие заключается в том, кто контролирует переход между задачами.

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

В этой модели каждая задача добровольно "отдает" контроль, позволяя другим задачам выполниться. Планировщик (scheduler) не может сам "вырвать" управление — он ждет, пока текущая задача явно уступит процессорное время.

  • Ключевые принципы:
    *   **Явная передача контроля:** Задача должна вызывать специальную функцию (например, `yield()`), чтобы передать управление планировщику.
    *   **Отсутствие "вытеснения":** Планировщик не может прервать выполняющуюся задачу.
    *   **Ответственность задач:** Если задача не отдает управление (например, из-за бесконечного цикла), система может "зависнуть", и другие задачи никогда не выполнятся.

  • Пример в Go (ранние версии планировщика): Первоначальный планировщик Go был кооперативным в пределах одного потока ОС. Он использовал точки вытеснения (preemption points), такие как операции с каналами или системные вызовы, где goroutine могла уступить контроль. Однако это не было полноценным вытеснением.
// Пример поведения, характерного для кооперативной модели (схематично)
func greedyTask() {
    for i := 0; i < 1000000; i++ {
        // Длительные вычисления без точек вытеснения
        // В чисто кооперативной системе другие goroutine не смогут работать здесь
    }
    // Только после завершения цикла или явного вызова runtime.Gosched()
    // управление может перейти к другим задачам.
}

Вытесняющая многозадачность (Preemptive Multitasking)

В этой модели планировщик имеет право сам "вырвать" выполнение текущей задачи и передать процессорное время другой, независимо от состояния первой задачи. Планировщик действует на основе временных интервалов или других событий.

  • Ключевые принципы:
    *   **Контроль планировщика:** Планировщик решает, когда прервать текущую задачу (например, по истечению временного "слота").
    *   **Принудительное переключение:** Задача может быть прервана в любой точке своего выполнения.
    *   **Стабильность системы:** Плохая задача (например, содержащая бесконечный цикл) не может заблокировать всю систему, так как планировщик вытеснит её.

  • Пример в Go (современный планировщик): Сейчас планировщик Go использует вытесняющую модель. Он может прерывать goroutine на определенных точках (например, на каждой проверке функции) или по сигналам от системы (например, при длительных вызовах syscall).
// В современном Go планировщик может вытеснить эту goroutine,
// даже если она выполняет "тяжелый" цикл без явных точек yield.
func greedyTask() {
    for i := 0; i < 1000000; i++ {
        // Планировщик может прервать выполнение здесь и передать
        // управление другой goroutine, обеспечивая прогресс всей программы.
        doSomeWork(i)
    }
}

Сравнительная таблица

КритерийКооперативная многозадачностьВытесняющая многозадачность
Контроль переключенияСама выполняющаяся задачаПланировщик (ОС или runtime)
Риск зависанияВысокий (если задача не уступает)Низкий (планировщик вытеснит)
РеализацияПроще, требует меньше синхронизацииСложнее, требует механизмов прерывания
Контроль времени выполненияЗадачи сами распределяют времяПланировщик распределяет время принудительно
ПримерыСтарые ОС (Windows 3.x), ранние runtime GoСовременные ОС (Linux, Windows NT), современный Go

В контексте Go: Современный планировщик goroutine является вытесняющим. Это было критически важно для устранения проблемы "зависания" программы, когда одна goroutine могла монополизировать поток ОС, блокируя выполнение других. Вытеснение позволяет гарантировать прогресс всех goroutine и является ключевым для реализации concurrency в Go, особенно в высоконагруженных системах. Однако вытеснение ограничено определенными безопасными точками в коде, чтобы не нарушать состояние программы.

В чем разница между кооперативной и вытесняющей многозадачностью? | PrepBro