Ответ
Инверсия приоритетов (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 эту проблему, но понимание инверсии критично при проектировании собственных сложных систем синхронизации.
Ответ 18+ 🔞
Да ты посмотри, какая дичь, блядь! Сидим мы тут, думаем, что приоритеты — это святое, а они, сука, выкидывают такие фортели, что просто пиздец.
Вот представь, есть у тебя три задачи, как три мужика в бане. Один — крутой мажор, приоритет высоченный (H). Второй — середнячок, не фонтан (M). Третий — последний нищеброд, приоритет ниже плинтуса (L).
И есть у них одна общая шайка-мыло, ресурс, значит. Всё по-братски.
- Заходит наш нищеброд L первым, хватает шайку и начинает ею так залихватски мылиться. Не спеша, с чувством.
- Тут врывается мажор H. Ему тоже срочно надо помыться. Тыкается к шайке — а она занята! Ну, бля, думает, щас этого лузера попрошу — он же поймёт, кто тут главный. Стоит, ждёт.
- И в этот самый момент, пока мажор H упёрся лбом в стенку и ждёт, в парилку вваливается середнячок M. Он вообще без шайки, просто попарить кости пришёл. Планировщик, этот, блядь, банщик, смотрит: мажор-то ждёт, а этот готов! И давай середнячка M в парилку гонять! Тот орет "Ох, жарко!", а дела-то делает.
И что получается, ёпта? Мажор с высшим приоритетом стоит и смотрит, как середнячок среднего приоритета наслаждается жизнью, потому что ждёт он, блядь, от нищеброда низшего приоритета. Приоритеты нахуй перевернулись! Это и есть инверсия, блядь, полный пиздец для систем реального времени. Всё летит к чертям, дедлайны горят, волнение ебать.
Вот код, который эту похабщину демонстрирует:
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 успел схватить
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 в это время вовсю парится
mediumPriorityQueue.async {
for i in 1...3 {
print("Medium priority task M: Working... ((i))")
sleep(1)
}
}
Как с этой хуйней бороться?
- Наследование приоритета: Это как взять нашего нищеброда L, который шайку держит, и надеть на него мажорскую цепь. Раз его ждёт мажор H, то и приоритет L временно поднимаем до уровня H. Пусть, сука, мытся быстрее, чтоб не задерживать важную персону! В
pthreadэто, кстати, есть. - Потолок приоритетов: Тут ещё проще. Объявляем, что шайка — это святыня. Кто её взял, тот сразу временно становится круче всех. Никакой середнячок M в этот момент даже пикнуть не посмеет.
В iOS, конечно, умные дядьки постарались, чтобы такие косяки реже всплывали, но, блядь, если сам накосячишь с блокировками — получишь эту самую инверсию в чистом виде. Так что думай головой, когда ресурсы делишь, а то будет тебе ебаный цирк, а не многопоточность.