Ответ
Инверсия приоритетов (Priority Inversion) — это проблемная ситуация в планировании задач с реальным временем (RTOS) или приоритетными очередями, когда задача с высоким приоритетом вынуждена ожидать выполнения задачи с низким приоритетом, что нарушает ожидаемую логику планировщика.
Классический сценарий (на примере трех задач):
- Задача L (Low Priority) захватывает разделяемый ресурс (например, блокировку
NSLock). - Задача H (High Priority) запускается и также пытается захватить тот же ресурс. Поскольку он занят, H блокируется и ждет.
- В этот момент запускается Задача M (Medium Priority), которая не использует общий ресурс. Планировщик, видя, что H заблокирована, а M готова к выполнению, отдает CPU задаче M.
- Результат: Задача M (средний приоритет) выполняется, в то время как задача H (высший приоритет) простаивает, ожидая L (низший приоритет). Приоритеты инвертировались.
Пример кода, моделирующего ситуацию:
let sharedResourceLock = NSLock()
let highPriorityQueue = DispatchQueue(label: "high", qos: .userInteractive)
let mediumPriorityQueue = DispatchQueue(label: "medium", qos: .utility)
let lowPriorityQueue = DispatchQueue(label: "low", qos: .background)
// Задача L (низкий приоритет) захватывает ресурс
lowPriorityQueue.async {
sharedResourceLock.lock()
print("Low priority task L: Lock acquired")
sleep(2) // Имитация долгой работы с ресурсом
sharedResourceLock.unlock()
print("Low priority task L: Lock released")
}
// Небольшая задержка, чтобы L успел захватить lock
Thread.sleep(forTimeInterval: 0.1)
// Задача H (высокий приоритет) блокируется на ресурсе
highPriorityQueue.async {
print("High priority task H: Waiting for lock...")
sharedResourceLock.lock() // БЛОКИРОВКА! Ждет L.
print("High priority task H: Lock acquired")
sharedResourceLock.unlock()
}
// Задача M (средний приоритет) может выполняться, пока H ждет
mediumPriorityQueue.async {
for i in 1...3 {
print("Medium priority task M: Working... ((i))")
sleep(1)
}
}
Решения проблемы:
- Наследование приоритета (Priority Inheritance): Когда задача H блокируется на ресурсе, удерживаемом L, приоритет L временно повышается до приоритета H. Это позволяет L быстрее завершиться и освободить ресурс. Эта политика используется, например, в
pthread_mutexс атрибутомPRIO_INHERIT. - Потолок приоритетов (Priority Ceiling): Ресурсу назначается заранее известный высокий "потолочный" приоритет. Любая задача, захватившая этот ресурс, временно получает этот высокий приоритет, предотвращая выполнение задач среднего приоритета.
В iOS/ Swift: Системные механизмы (например, os_unfair_lock) и GCD в определенной степени помогают mitigate эту проблему, но понимание инверсии критично при проектировании собственных сложных систем синхронизации.