Сообщить об ошибке.

Класс RLock() модуля threading в Python

Повторная/реентерабельная блокировка потока

Синтаксис:

import threading

rlck = threading.RLock()

Параметры:

  • нет

Возвращаемое значение:

Описание:

Класс RLock() модуля threading реализует объекты реентерабельной (повторной) блокировки.

Повторяющаяся блокировка должна быть снята потоком, который ее получил. Как только поток получил повторную блокировку, тот же поток может получить ее снова без блокировки. Поток должен освобождать ее столько раз, сколько раз он ее приобрел.

Обратите внимание, что threading.RLock на самом деле является фабричным классом, который возвращает экземпляр наиболее эффективной версии конкретного класса threading.RLock, поддерживаемого платформой.

Повторная блокировка потока - это примитив синхронизации, который может быть получен несколько раз одним и тем же потоком. Внутри он использует концепции "владеющего потоком" и "уровня рекурсии" в дополнение к locked/unlocked состоянию, используемому примитивными блокировками threading.Lock. В заблокированном locked состоянии какой-то поток владеет блокировкой, в разблокированном unlocked состоянии ни один поток не владеет им.

Чтобы включить блокировку, поток вызывает свой метод RLock.acquire(), он возвращает результат своему экземпляру, когда поток владеет блокировкой. Чтобы снять блокировку, поток вызывает свой метод RLock.release().

Пары вызовов RLock.acquire()/RLock.release() могут быть вложенными, только последний RLock.release() (release() самой внешней пары) сбрасывает режим locked на unlocked и позволяет продолжить работу другому потоку, заблокированному в RLock.acquire().

Класс threading.RLock() также поддерживают протокол управления контекстом.

Методы объекта threading.RLock.


RLock.acquire(blocking=True, timeout=-1):

Метод RLock.acquire() устанавливает блокировку, блокирующую или неблокирующую..

При вызове без аргументов:

  • Если этот поток уже владеет блокировкой, то увеличивает уровень рекурсии на единицу и немедленно возвращает результат своему экземпляру.
  • Если другой поток владеет блокировкой, то текущий поток блокируется, пока не будет снята блокировка. Как только блокировка снята (не принадлежит ни одному потоку), то захватывает владение блокировкой, устанавливает уровень рекурсии на единицу и возвращает результат своему экземпляру.
  • Если более одного потока находятся в ожидании снятия блокировки, то только один из них сможет получить право владения блокировкой. В этом случае нет возвращаемого значения.

При вызове метода с параметром blocking, установленным в значение True, выполнит то же действие, что и при вызове без аргументов и возвратит значение True.

При вызове метода с аргументом blocking, установленным в False, не ставит блокировку, а проверит, сможет ли метод с blocking=True поставить блокировку, если нет, то немедленно вернет False, в противном случае установит блокировку и возвратит True.

При вызове с аргументом тайм-аута timeout с числом float, установленным в положительное значение, будет блокировать выполнение кода не более чем на количество секунд, заданное таймаутом и до тех пор, пока блокировка не будет получена. В этом случае, возвращает True, если блокировка была получена и False, если истекло время ожидания timeout.

RLock.release():

Метод RLock.release() снимает блокировку, уменьшив уровень рекурсии. Если после декремента он равен нулю, то сбрасывает блокировку на unlocked (т.е. блокировка не принадлежащую ни одному потоку), и если какие-либо другие потоки заблокированы, ожидая разблокировки, то разрешит выполнение ровно одному из них. Если после декремента уровень рекурсии все еще отличен от нуля, то блокировка остается locked и принадлежит вызывающему потоку.

Вызывайте этот метод только тогда, когда вызывающий поток владеет блокировкой. Если этот метод вызывается при уже снятой блокировке, то возникает исключение RuntimeError

Возвращаемого значения нет.

Пример работы повторной блокировки threading.RLock().

Объекты threading.Lock() не могут быть получены более одного раза, даже одним и тем же потоком. Это может привести к нежелательным побочным эффектам, если доступ к блокировке осуществляется более чем одной функцией в одной цепочке вызовов.

В этом случае второму вызову Lock.acquire() необходимо дать нулевой тайм-аут, чтобы предотвратить его блокировку, потому что блокировка была уже получена первым вызовом.

>>> import threading
>>> lock = threading.Lock()
>>> lock
# <unlocked _thread.lock object at 0x7f836cdb0b48>

# первая блокировка
>>> 'First try :', lock.acquire()
# ('First try :', True)
>>> lock
# <locked _thread.lock object at 0x7f836cdb0b48>

# вторая блокировка
'Second try:', lock.acquire(timeout=0)
# ('Second try:', False)

В ситуации, когда отдельный код из одного и того же потока должен “повторно получить” блокировку, необходимо использовать объекты threading.RLock.

Единственным изменением в коде из предыдущего примера была замена объекта Rlock на Lock.

>>> import threading
>>> rlock = threading.RLock()
# первая блокировка
>>> 'First try :', rlock.acquire()
# ('First try :', True)

# вторая блокировка
>>>'Second try:', rlock.acquire(timeout=0)
# ('Second try:', True)

# смотрим состояние - счетчик повторных блокировок count=2
>>> rlock
# <locked _thread.RLock object owner=140202475812672 count=2 at 0x7f83693acb10>

# разблокируем 2 раза
>>> rlock.release()
>>> rlock.release()
# смотрим состояние - счетчик повторных блокировок count=0
>>> rlock
# <locked _thread.RLock object owner=140202475812672 count=0 at 0x7f83693acb10>

# пробуем разблокировать уже разблокированное состояние
>>> rlock.release()
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# RuntimeError: cannot release un-acquired lock