Документация Python неофициальный перевод
Содержание страницы

Примитивы синхронизацииSynchronization Primitives

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


Примитивы синхронизации asyncio устроены аналогично примитивам модуля threading, но с двумя важными оговорками:

  • Примитивы asyncio не являются потокобезопасными, поэтому их не следует использовать для синхронизации потоков ОС (для этого используйте threading);

  • Методы этих примитивов синхронизации не принимают аргумент timeout; для выполнения операций с таймаутами используйте функцию asyncio.wait_for().

В asyncio имеются следующие базовые примитивы синхронизации:


БлокировкаLock

class asyncio.Lock(*, loop=None)

Реализует блокировку мьютекса для задач asyncio. Не потокобезопасна.

Блокировка asyncio может использоваться для обеспечения исключительного доступа к общему ресурсу.

The preferred way to use a Lock is an async with statement:

lock = asyncio.Lock()

# ... позже
async with lock:
    # доступ к общему состоянию

это эквивалентно следующему:

lock = asyncio.Lock()

# ... позже
await lock.acquire()
try:
    # доступ к общему состоянию
finally:
    lock.release()

Устарело с версии 3.8, будет удалено в версии 3.10: Параметр loop.

coroutine acquire()

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

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

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

Захват блокировки справедлив: выполнение продолжит первая корутина, начавшая ожидание блокировки.

release()

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

Если блокировка заблокирована, сбрасывает её в состояние разблокирована и завершается.

Если блокировка разблокирована, возбуждается исключение RuntimeError.

locked()

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

Event

class asyncio.Event(*, loop=None)

Объект события. Не потокобезопасен.

Событие asyncio может использоваться для оповещения нескольких задач asyncio о наступлении некоторого события.

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

Устарело с версии 3.8, будет удалено в версии 3.10: Параметр loop.

Пример:

async def waiter(event):
    print('waiting for it ...')
    await event.wait()
    print('... got it!')

async def main():
    # Создать объект Event.
    event = asyncio.Event()

    # Создать задачу, ожидающую установки 'event'.
    waiter_task = asyncio.create_task(waiter(event))

    # Подождать 1 секунду и установить событие.
    await asyncio.sleep(1)
    event.set()

    # Дождаться завершения ожидающей задачи.
    await waiter_task

asyncio.run(main())
coroutine wait()

Ожидает, пока событие не будет установлено.

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

set()

Устанавливает событие.

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

clear()

Сбрасывает (снимает) событие.

Задачи, ожидающие на wait(), теперь будут блокироваться, пока метод set() не будет вызван снова.

is_set()

Возвращает True, если событие установлено.

Condition

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

Объект Condition. Не потокобезопасен.

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

По сути, объект Condition объединяет функциональность Event и Lock. Несколько объектов Condition могут совместно использовать одну блокировку, что позволяет координировать исключительный доступ к общему ресурсу между различными задачами, заинтересованными в определённых состояниях этого ресурса.

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

Устарело с версии 3.8, будет удалено в версии 3.10: Параметр loop.

Предпочтительный способ использования Condition – оператор async with:

cond = asyncio.Condition()

# ... позже
async with cond:
    await cond.wait()

это эквивалентно следующему:

cond = asyncio.Condition()

# ... позже
await cond.acquire()
try:
    await cond.wait()
finally:
    cond.release()
coroutine acquire()

Захватывает базовую блокировку.

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

notify(n=1)

Будит не более n задач (по умолчанию 1), ожидающих на этом условии. Если ожидающих задач нет, метод ничего не делает.

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

locked()

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

notify_all()

Пробуждает все задачи, ожидающие данное условие.

Этот метод действует как notify(), но пробуждает все ожидающие задачи.

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

release()

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

При вызове на незаблокированной блокировке возникает RuntimeError.

coroutine wait()

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

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

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

coroutine wait_for(predicate)

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

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

Semaphore

class asyncio.Semaphore(value=1, *, loop=None)

Объект семафора. Не потокобезопасен.

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

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

Устарело с версии 3.8, будет удалено в версии 3.10: Параметр loop.

Предпочтительный способ использования семафора – оператор async with:

sem = asyncio.Semaphore(10)

# ... позже
async with sem:
    # работать с общим ресурсом

это эквивалентно следующему:

sem = asyncio.Semaphore(10)

# ... позже
await sem.acquire()
try:
    # работать с общим ресурсом
finally:
    sem.release()
coroutine acquire()

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

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

locked()

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

release()

Освобождает семафор, увеличивая внутренний счётчик на единицу. Может пробудить задачу, ожидающую захвата семафора.

В отличие от BoundedSemaphore, Semaphore допускает больше вызовов release(), чем вызовов acquire().

BoundedSemaphore

class asyncio.BoundedSemaphore(value=1, *, loop=None)

Объект ограниченного семафора. Не потокобезопасен.

Bounded Semaphore – это версия Semaphore, которая возбуждает ValueError в release(), если увеличивает внутренний счётчик выше начального value.

Устарело с версии 3.8, будет удалено в версии 3.10: Параметр loop.


Устарело с версии 3.7: Получение блокировки с помощью await lock или yield from lock и/или with (with await lock, with (yield from lock)) устарело. Вместо этого используйте async with lock.