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

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


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

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

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

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


Замок

class asyncio.Lock

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

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

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

lock = asyncio.Lock()

# ... later
async with lock:
    # access shared state

что эквивалентно:

lock = asyncio.Lock()

# ... later
await lock.acquire()
try:
    # access shared state
finally:
    lock.release()

Изменено в версии 3.10: Удален параметр loop.

coroutine acquire()

Приобретите замок.

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

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

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

release()

Освободите замок.

Когда замок заблокирован, сбросьте его на разблокирован и вернитесь.

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

locked()

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

Событие

class asyncio.Event

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

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

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

Изменено в версии 3.10: Удален параметр loop.

Пример:

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

async def main():
    # Create an Event object.
    event = asyncio.Event()

    # Spawn a Task to wait until 'event' is set.
    waiter_task = asyncio.create_task(waiter(event))

    # Sleep for 1 second and set the event.
    await asyncio.sleep(1)
    event.set()

    # Wait until the waiter task is finished.
    await waiter_task

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

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

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

set()

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

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

clear()

Очистить (снять установку) событие.

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

is_set()

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

Состояние

class asyncio.Condition(lock=None)

Объект Condition. Не является потокобезопасным.

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

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

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

Изменено в версии 3.10: Удален параметр loop.

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

cond = asyncio.Condition()

# ... later
async with cond:
    await cond.wait()

что эквивалентно:

cond = asyncio.Condition()

# ... later
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)

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

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

Семафор

class asyncio.Semaphore(value=1)

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

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

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

Изменено в версии 3.10: Удален параметр loop.

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

sem = asyncio.Semaphore(10)

# ... later
async with sem:
    # work with shared resource

что эквивалентно:

sem = asyncio.Semaphore(10)

# ... later
await sem.acquire()
try:
    # work with shared resource
finally:
    sem.release()
coroutine acquire()

Приобретите семафор.

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

locked()

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

release()

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

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

BoundedSemaphore

class asyncio.BoundedSemaphore(value=1)

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

Bounded Semaphore - это версия Semaphore, которая поднимает ValueError в release(), если увеличивает внутренний счетчик выше начального значения.

Изменено в версии 3.10: Удален параметр loop.


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

Вернуться на верх