В этой лекции мы рассмотрим еще одну проблему, с которой можно столкнуться в многопоточном приложении. Priority inversion или инверсия приоритетов. Обычно в операционных системах все выполняемые или запланированные к выполнению потоки делятся по приоритетом и поток с более высоким приоритетом может вытеснять поток с более низким приоритетом и выполнять свою работу раньше. Представим, что у нас есть два потока: с высоким - поток 1 и узким - поток 2 приоритетом. В момент времени T1 поток 2 блокирует некоторый общий с потоком один ресурс и начинает с ним работать. В момент времени T2 потока 1 вытесняет низко приоритетный поток 2 и пытается завладеть ресурсом в момент времени T3, но так как ресурс заблокирован, поток 1 переводится в ожидания, а поток 2 продолжает выполнение. Момент времени Т4 поток 2 завершает работу с ресурсом и разблокирует его, так как этот ресурс ожидает поток 1, он тут же начинает его выполнение. Временной промежуток с Т3 по Т4 называется ограниченная инверсия приоритетов. В этом промежутке наблюдается логическое несоответствие с правилами планирования. Поток с более высоким приоритетом находится в ожидании, в то время, как низко приоритетный поток выполняется. Есть более сложная ситуация. Представим, что у нас имеется три потока с различными приоритетами: высоким - поток 1, средним - поток 2 и низким - поток 3, и пусть потоки 1 и 3 делят некий общий ресурс. Если поток 3 с низким приоритетом завладел ресурсом раньше потока 1 с высоким приоритетом, то мы получим описанную выше ситуацию. Поток 1 блокируется и ожидает пока поток 3 освободит общий ресурс. В такой ситуации операционная система решает запустить поток 2 со средним приоритетом, так как его приоритет выше, чем у потока 3, а поток 1 оказался заблокирован. Поток 2 ничего не знает о конфликте, поэтому может выполняться сколь угодно долго на промежутке времени с Т4 по Т5. Кроме того, помимо потока 2, в системе могут быть и другие потоки с приоритетом больше потока 3, но меньше потока 1, поэтому длительность периода с Т3 по Т6 в общем случае не определена. Таким образом, мы попадаем в ситуацию, в которой поток 1 с более высоким приоритетом косвенно вытесняется потоком 2 с более низким приоритетом, инвертируя их относительные приоритеты. Такую ситуацию называют неограниченная инверсия приоритетов. Ограниченной инверсии приоритетов в общем случае избежать невозможно, но она не представляет особой опасности по сравнению с неограниченной. Для того, чтобы избежать не ограниченной инверсии приоритетов, используются два основных способа основанных на изменении приоритетов задач: это наследование приоритета и потолок приоритета. Наследование приоритета - priority inheritance, пусть в системе существуют два потока: с высоким - поток 1, и низким - поток 2, приоритетом, которым требуется определенный ресурс. Пусть поток 2 с низким приоритетом захватил ресурс в момент времени T1. В момент T2 поток 1 вытесняет низко приоритетный поток 2 и затем, в момент времени T3 пытается захватить заблокированный потоком 2 ресурс. Механизм наследования приоритета состоит в том, что приоритет потока 2 повышается системой до приоритета потока 1 в момент времени Т3, то есть когда поток 1 пытается захватить заблокированный ресурс. Таким образом поток с приоритетом больше потока 2, но меньше потока 1 не могут реализовать не ограниченную инверсию, и поток 1 получит ресурс сразу после того, как его разблокирует поток 2. После того, как поток 2 разблокирует ресурс, его приоритет понижается до исходного. Механизм priority ceiling или потолок приоритета основан на том предположении, что на момент проектирования известны все потоки, которые требуется определенный ресурс, а также известны приоритеты этих потоков. В этом случае ресурсу можно назначить определённые свойства, максимальный приоритет из тех потоков, которые могут его заблокировать, т.е. назначить потолок. Допустим, что в системе существует три потока: с высоким - поток 1, средним - поток 2 и низким - поток 3, приоритетом, которые могут заблокировать 1 ресурс. И пусть поток 3 с низшим приоритетом захватил ресурс в момент времени Т1. Поток 2 со средним приоритетом стартует и вытесняет поток 3 в момент времени Т2. Момент времени Т3, когда поток 2 пытается захватить заблокированный потоком ресурс, приоритет потока 3 повышается до приоритета 1, то есть до максимального приоритета из тех потоков, которые могут владеть ресурсом. Как только поток 3 освобождает ресурс в момент времени Т4, его приоритет понижается до исходного и приоритет потока 2, ожидавшего ресурс, повышается до приоритета потока 1. После того, как поток 2 освобождает ресурс в момент времени Т5, его приоритет также понижается до первоначального значения. Таким образом, при использовании механизма priority ceiling поток, захвативший ресурс, всегда имеет наивысший приоритет из всех потоков, которые могут этим ресурсом владеть. Это позволяет не только избавиться от неограниченной инверсии приоритетов, но и не допустить взаимных блокировок. В следующих лекциях мы расскажем о других проблемах многопоточности и способах их решения.