На этом шаге мы рассмотрим использование этого класса.
При возникновении исключения внутри метода change_x() ресурс останется заблокированным, т. к. вызов метода unlock() не будет выполнен. Кроме того, можно по случайности забыть вызвать метод unlock(), что также приведет к вечной блокировке.
Исключить подобную ситуацию позволяет класс QMutexLocker. Конструктор этого класса принимает объект мьютекса и устанавливает блокировку. После выхода из области видимости будет вызван деструктор класса, внутри которого блокировка автематически снимается. Следовательно, если создать экземпляр класса QMutexLocker в начале метода, то после выхода из метода блокировка будет снята. Переделаем метод change_x() из класса MyThread и используем класс QMutexLocker.
def change_x(self): m1 = QtCore.QMutexLocker(MyThread.mutex) print("x =", MyThread.x, "id =", self.id) MyThread.x += 5 self.sleep(2) print("x =", MyThread.x, "id =", self.id) MyThread.x += 34 print("x =", MyThread.x, "id =", self.id) # Блокировка автоматически снимется
При использовании класса QMutexLocker следует помнить о разнице между областями видимости в языках C++ и Python. В языке C++ область видимости ограничена блоком, которым может являться как функция, так и просто область, ограниченная фигурными скобками. Таким образом, если переменная объявлена внутри блока условного оператора, например, if, то при выходе из этого блока переменная уже не будет видна:
if (условие) { int х = 10; // Объявляем переменную //.... } // Здесь переменная х уже не видна!
В языке Python область видимости гораздо шире. Если мы объявим переменную внутри условного оператора, то она будет видна и после выхода из этого блока:
if условие: х = 10 # Объявляем переменную # ... # Здесь переменная х еще видна
Таким образом, область видимости локальной переменной в языке Python ограничена функцией, а не любым блоком.
Класс QMutexLocker поддерживает протокол менеджеров контекста, который позволяет ограничить область видимости блоком инструкции with...as. Этот протокол гарантирует снятие блокировки даже при наличии исключения внутри инструкции with...as. Переделаем метод change_x() из класса MyThread снова и используем в этот раз инструкцию with...as.
def change_x(self): with QtCore.QMutexLocker(MyThread.mutex): print("x =", MyThread.x, "id =", self.id) MyThread.x += 5 self.sleep(2) print("x =", MyThread.x, "id =", self.id) MyThread.x += 34 print("x =", MyThread.x, "id =", self.id) # Блокировка автоматически снимется
Теперь, когда вы уже знаете о возможности блокировки ресурса, следует сделать несколько замечаний:
Класс QMutexLocker также поддерживает методы unlock() и relock(). Первый метод выполняет разблокировку мьютекса без уничтожения экземпляра класса QMutexLocker, а второй выполняет повторное наложение блокировки.
На следующем шаге мы рассмотрим вывод заставки.