Перейти к содержимому

18.5.7. Примитивы синхронизации

Исходный код: Lib/asyncio/locks.py

Блокировки:

Семафоры:

API блокировок asyncio спроектирован по аналогии с классами модуля threading (Lock, Event, Condition, Semaphore, BoundedSemaphore), но не имеет параметра тайм-аут. Функция asyncio.wait_for() может использоваться для отмены задачи после тайм-аута.

18.5.7.1. Блокировки

18.5.7.1.1. Блокировка

classasyncio.Lock(*, loop=None)

Объекты примитивной блокировки.

Примитивная блокировка – это примитив синхронизации, который не принадлежит какой-либо конкретной корутине, когда заблокирован. Примитивная блокировка находится в одном из двух состояний: «заблокирована» или «разблокирована».

Она создаётся в разблокированном состоянии. У неё есть два основных метода: acquire() и release(). Когда состояние разблокировано, acquire() меняет состояние на заблокированное и немедленно возвращается. Когда состояние заблокировано, acquire() блокируется до тех пор, пока вызов release() в другой корутине не изменит его на разблокированное; затем вызов acquire() сбрасывает его в заблокированное и возвращается. Метод release() следует вызывать только в заблокированном состоянии; он меняет состояние на разблокированное и немедленно возвращается. Если предпринята попытка освободить разблокированную блокировку, будет возбуждено RuntimeError.

Когда несколько корутин заблокированы в acquire() в ожидании перехода состояния в разблокированное, только одна корутина продолжает выполнение, когда вызов release() сбрасывает состояние в разблокированное; обрабатывается первая корутина, заблокированная в acquire().

acquire() – корутина, и её следует вызывать с yield from.

Блокировки также поддерживают протокол менеджера контекста. (yield from lock) должен использоваться в качестве выражения менеджера контекста.

Этот класс является не потокобезопасным.

Использование:

python
lock = Lock()
...
yield from lock
try:
    ...
finally:
    lock.release()

Использование менеджера контекста:

python
lock = Lock()
...
with (yield from lock):
    ...

Объекты блокировки можно проверить на состояние блокировки:

python
if not lock.locked():
    yield from lock
else:
    # блокировка захвачена
    ...
locked()

Возвращает True, если блокировка захвачена.

coroutineacquire()

Захватить блокировку.

Этот метод блокируется до тех пор, пока блокировка не будет разблокирована, затем устанавливает её в заблокированное состояние и возвращает True.

Этот метод является корутиной.

release()

Освободить блокировку.

Когда блокировка заблокирована, сбросить её в разблокированное состояние и вернуться. Если какие-либо другие корутины заблокированы в ожидании разблокировки блокировки, позволить ровно одной из них продолжить.

При вызове на разблокированной блокировке возбуждается RuntimeError.

Возвращаемое значение отсутствует.

18.5.7.1.2. Event

classasyncio.Event(*, loop=None)

Реализация Event, асинхронный эквивалент threading.Event.

Класс, реализующий объекты событий. Событие управляет флагом, который может быть установлен в true методом set() и сброшен в false методом clear(). Метод wait() блокируется до тех пор, пока флаг не станет true. Изначально флаг имеет значение false.

Этот класс является не потокобезопасным.

clear()

Сбросить внутренний флаг в false. Впоследствии корутины, вызывающие wait(), будут блокироваться до тех пор, пока не будет вызван set() для повторной установки внутреннего флага в true.

is_set()

Возвращает True тогда и только тогда, когда внутренний флаг равен true.

set()

Установить внутренний флаг в true. Все корутины, ожидающие его перехода в true, пробуждаются. Корутины, вызывающие wait() после установки флага в true, не будут блокироваться вовсе.

coroutinewait()

Блокироваться до тех пор, пока внутренний флаг не станет true.

Если внутренний флаг уже true при входе, немедленно вернуть True. В противном случае блокироваться до тех пор, пока другая корутина не вызовет set() для установки флага в true, затем вернуть True.

Этот метод является корутиной.

18.5.7.1.3. Condition

class asyncio.Condition(lock=None, *, loop=None)

Реализация Condition, асинхронный эквивалент threading.Condition.

Этот класс реализует объекты условных переменных. Условная переменная позволяет одной или нескольким корутинам ждать, пока другая корутина не уведомит их.

Если аргумент блокировка задан и не равен None, он должен быть объектом Lock и используется в качестве базовой блокировки. В противном случае создаётся новый объект Lock и используется в качестве базовой блокировки.

Этот класс является не потокобезопасным.

coroutineacquire()

Захватить нижележащую блокировку.

Этот метод блокируется до тех пор, пока блокировка не будет разблокирована, затем устанавливает её в заблокированное состояние и возвращает True.

Этот метод является корутиной.

notify(n=1)

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

Этот метод пробуждает не более n корутин, ожидающих переменную условия; если ожидающих корутин нет, он ничего не делает.

Примечание

Пробуждённая корутина на самом деле не возвращается из вызова wait(), пока не сможет повторно захватить блокировку. Поскольку notify() не освобождает блокировку, это должен сделать её вызывающий код.

locked()

Вернуть True, если нижележащая блокировка захвачена.

notify_all()

Пробуждает все корутины, ожидающие на этом условии. Этот метод работает как notify(), но пробуждает все ожидающие корутины вместо одной. Если вызывающая корутина не захватила блокировку на момент вызова этого метода, возбуждается RuntimeError.

release()

Освободить нижележащую блокировку.

Если блокировка захвачена, сбрасывает её в незахваченное состояние и возвращает управление. Если какие-либо другие корутины заблокированы в ожидании освобождения блокировки, позволяет ровно одной из них продолжить работу.

При вызове на разблокированной блокировке возбуждается RuntimeError.

Возвращаемое значение отсутствует.

coroutinewait()

Ожидать уведомления.

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

Этот метод освобождает нижележащую блокировку, а затем блокируется до тех пор, пока не будет пробуждён вызовом notify() или notify_all() для той же переменной условия в другой корутине. После пробуждения он повторно захватывает блокировку и возвращает True.

Этот метод является корутиной.

coroutinewait_for(predicate)

Ожидает, пока предикат не станет истинным.

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

Этот метод является корутиной.

18.5.7.2. Семафоры

18.5.7.2.1. Semaphore

classasyncio.Semaphore(value=1, *, loop=None)

Реализация семафора.

Семафор управляет внутренним счётчиком, который уменьшается при каждом вызове acquire() и увеличивается при каждом вызове release(). Счётчик никогда не может стать меньше нуля; когда acquire() обнаруживает, что он равен нулю, блокируется в ожидании, пока другая корутина вызовет release().

Семафоры также поддерживают протокол менеджера контекста.

Необязательный аргумент задаёт начальное значение внутреннего счётчика; по умолчанию 1. Если указанное значение меньше 0, возбуждается ValueError.

Этот класс является не потокобезопасным.

coroutineacquire()

Захватить семафор.

Если при входе внутренний счётчик больше нуля, уменьшить его на единицу и немедленно вернуть True. Если при входе он равен нулю, блокироваться в ожидании, пока другая корутина не вызовет release(), чтобы сделать его больше 0, после чего вернуть True.

Этот метод является корутиной.

locked()

Возвращает True, если семафор не может быть захвачен немедленно.

release()

Освобождает семафор, увеличивая внутренний счётчик на единицу. Если при входе он был равен нулю и другая корутина ожидает, пока он снова станет больше нуля, пробудить эту корутину.

18.5.7.2.2. BoundedSemaphore

classasyncio.BoundedSemaphore(value=1, *, loop=None)

Реализация ограниченного семафора. Наследует от Semaphore.

Это возбуждает ValueError в release(), если это привело бы к увеличению значения выше начального.