Запросите, заблокирован ли Python threading.Lock или нет.

У меня есть поток, который я запускаю (код ниже), который запускает блокирующий подпроцесс. Чтобы другие потоки не запускали один и тот же подпроцесс, у меня есть блокировка этого вызова subprocess.call. Я также хочу, чтобы иметь возможность завершить этот вызов подпроцесса, поэтому у меня есть функция остановки, которую я вызываю из другого места. В случае, если подпроцесс остановлен преждевременно, я хочу также освободить блокировку, что и делает следующий код:

class SomeThread(threading.Thread):
   def run(self):
      aLock.acquire()
      self.clip = subprocess.call([ 'mplayer', 'Avatar.h264'], stdin=subprocess.PIPE)
      aLock.release()
   def stop(self):
      if self.clip != None and self.clip.poll() == True:
         try:
            self.clip.send_signal(signal.SIGINT)
         except:
            pass
      aLock.release()

Однако, согласно приведенной здесь документации, вызов release() на выпущенной блокировке вызовет исключение:

A RuntimeError is raised if this method is called when the lock is unlocked.

Есть ли функция запроса, такая как aLock.isLocked()?

Ответы

Ответ 1

Конечно!

>>> from threading import Lock
>>> x = Lock()
>>> x.locked()
False
>>> x.acquire()
True
>>> x.locked()
True

Вы также можете сделать неблокирующее приобретение:

x.acquire(False)
x.release()

В этом случае, если x был разблокирован, код получает его и освобождает. Но если x уже был заблокирован, неблокирующее получение возвращает сразу (и возвращает False), и мы снова отпускаем его. Но это подвержено гонкам! Нет ничего, что могло бы помешать другому потоку снять блокировку между этими двумя строками.

То же .locked() для проверки .locked(). Это говорит только о состоянии блокировки во время выполнения .locked(). Это может больше не быть правдой ко времени выполнения следующего оператора.

Кстати, тело run() лучше написать с использованием блокировки в качестве "диспетчера контекста", например так:

def run(self):
    with aLock:
        self.clip = subprocess.call([ 'mplayer', 'Avatar.h264'], stdin=subprocess.PIPE)

Это делает для вас пару acquire()/release() и намного более устойчиво к неожиданным исключениям, возникшим в теле блока with (Python делает все возможное, чтобы снять блокировку, если тело по какой-либо причине закрывается).