asyncio-sync.md
1> **Источник:** https://python-all.ru/3.16/library/asyncio-sync.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Примитивы синхронизации89**Исходный код:** [Lib/asyncio/locks.py](https://python-all.ru/src/main/Lib/asyncio/locks.py)1011---1213Примитивы синхронизации asyncio устроены аналогично примитивам модуля [`threading`](https://python-all.ru/3.16/library/threading.html#module-threading), но с двумя важными оговорками:1415- Примитивы asyncio не являются потокобезопасными, поэтому их не следует использовать для синхронизации потоков ОС (для этого используйте [`threading`](https://python-all.ru/3.16/library/threading.html#module-threading));16- Методы этих примитивов синхронизации не принимают аргумент *timeout*; для выполнения операций с таймаутами используйте функцию [`asyncio.wait_for()`](https://python-all.ru/3.16/library/asyncio-task.html#asyncio.wait_for).1718В asyncio имеются следующие базовые примитивы синхронизации:1920- [`Lock`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Lock)21- [`Event`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Event)22- [`Condition`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Condition)23- [`Semaphore`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Semaphore)24- [`BoundedSemaphore`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.BoundedSemaphore)25- [`Barrier`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Barrier)2627---2829## Блокировка3031#### `class asyncio.Lock`3233Реализует блокировку мьютекса для задач asyncio. Не потокобезопасна.3435Блокировка asyncio может использоваться для обеспечения исключительного доступа к общему ресурсу.3637The preferred way to use a Lock is an [`async with`](https://python-all.ru/3.16/reference/compound_stmts.html#async-with) statement:3839```python40lock = asyncio.Lock()4142# ... позже43async with lock:44 # доступ к общему состоянию45```4647что эквивалентно:4849```python50lock = asyncio.Lock()5152# ... позже53await lock.acquire()54try:55 # доступ к общему состоянию56finally:57 lock.release()58```5960Изменено в версии 3.10: Удалён параметр *loop*.6162#### `async acquire()`6364Захватывает блокировку.6566Этот метод ожидает, пока блокировка не будет *разблокирована*, переводит её в состояние *заблокирована* и возвращает `True`.6768Если несколько корутин заблокированы вызовом `acquire()` в ожидании разблокировки блокировки, только одна из них в итоге продолжит выполнение.6970Захват блокировки *справедлив*: выполнение продолжит первая корутина, начавшая ожидание блокировки.7172#### `release()`7374Освобождает блокировку.7576Если блокировка *заблокирована*, сбрасывает её в состояние *разблокирована* и завершается.7778Если блокировка *разблокирована*, возбуждается исключение [`RuntimeError`](https://python-all.ru/3.16/library/exceptions.html#RuntimeError).7980#### `locked()`8182Возвращает `True`, если блокировка *заблокирована*.8384## Event8586#### `class asyncio.Event`8788Объект события. Не потокобезопасен.8990Событие asyncio может использоваться для оповещения нескольких задач asyncio о наступлении некоторого события.9192Объект Event управляет внутренним флагом, который может быть установлен в *true* методом [`set()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Event.set) и сброшен в *false* методом [`clear()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Event.clear). Метод [`wait()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Event.wait) блокируется, пока флаг не будет установлен в *true*. Изначально флаг установлен в *false*.9394Изменено в версии 3.10: Удалён параметр *loop*.9596Пример:9798```python99async def waiter(event):100 print('waiting for it ...')101 await event.wait()102 print('... got it!')103104async def main():105 # Создать объект Event.106 event = asyncio.Event()107108 # Создать задачу, ожидающую установки 'event'.109 waiter_task = asyncio.create_task(waiter(event))110111 # Подождать 1 секунду и установить событие.112 await asyncio.sleep(1)113 event.set()114115 # Дождаться завершения ожидающей задачи.116 await waiter_task117118asyncio.run(main())119```120121#### `async wait()`122123Ожидает, пока событие не будет установлено.124125Если событие установлено, немедленно возвращает `True`. В противном случае блокируется до тех пор, пока другая задача не вызовет [`set()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Event.set).126127#### `set()`128129Устанавливает событие.130131Все задачи, ожидающие установки события, будут немедленно пробуждены.132133#### `clear()`134135Сбрасывает (снимает) событие.136137Последующие задачи, ожидающие [`wait()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Event.wait), будут блокироваться до тех пор, пока метод [`set()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Event.set) не будет вызван снова.138139#### `is_set()`140141Возвращает `True`, если событие установлено.142143## Condition144145#### `class asyncio.Condition(lock=None)`146147Объект Condition. Не потокобезопасен.148149Примитив условия asyncio позволяет задаче дождаться наступления некоторого события, а затем получить исключительный доступ к общему ресурсу.150151По сути, объект Condition объединяет функциональность [`Event`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Event) и [`Lock`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Lock). Несколько объектов Condition могут совместно использовать одну блокировку, что позволяет координировать исключительный доступ к общему ресурсу между различными задачами, заинтересованными в определённых состояниях этого ресурса.152153Необязательный аргумент *блокировка* должен быть объектом [`Lock`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Lock) или `None`. В последнем случае новый объект блокировки создаётся автоматически.154155Изменено в версии 3.10: Удалён параметр *loop*.156157Предпочтительный способ использования Condition – оператор [`async with`](https://python-all.ru/3.16/reference/compound_stmts.html#async-with):158159```python160cond = asyncio.Condition()161162# ... позже163async with cond:164 await cond.wait()165```166167что эквивалентно:168169```python170cond = asyncio.Condition()171172# ... позже173await cond.acquire()174try:175 await cond.wait()176finally:177 cond.release()178```179180#### `async acquire()`181182Захватывает базовую блокировку.183184Этот метод ожидает, пока базовая блокировка не будет *разблокирована*, устанавливает её в состояние *заблокирована* и возвращает `True`.185186#### `notify(n=1)`187188Пробуждает *n* задач (по умолчанию 1), ожидающих данное условие. Если ожидает менее *n* задач, пробуждаются все.189190Блокировка должна быть захвачена до вызова этого метода и освобождена сразу после. Если вызвать с *разблокированной* блокировкой, будет возбуждено исключение [`RuntimeError`](https://python-all.ru/3.16/library/exceptions.html#RuntimeError).191192#### `locked()`193194Возвращает `True`, если базовая блокировка захвачена.195196#### `notify_all()`197198Пробуждает все задачи, ожидающие данное условие.199200Этот метод действует как [`notify()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Condition.notify), но пробуждает все ожидающие задачи.201202Блокировка должна быть захвачена до вызова этого метода и освобождена сразу после. Если вызвать с *разблокированной* блокировкой, будет возбуждено исключение [`RuntimeError`](https://python-all.ru/3.16/library/exceptions.html#RuntimeError).203204#### `release()`205206Освобождает базовую блокировку.207208При вызове на незаблокированной блокировке возникает [`RuntimeError`](https://python-all.ru/3.16/library/exceptions.html#RuntimeError).209210#### `async wait()`211212Ожидает уведомления.213214Если вызывающая задача не захватила блокировку на момент вызова этого метода, возникает [`RuntimeError`](https://python-all.ru/3.16/library/exceptions.html#RuntimeError).215216Этот метод освобождает базовую блокировку и блокируется до тех пор, пока не будет пробуждён вызовом [`notify()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Condition.notify) или [`notify_all()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Condition.notify_all). После пробуждения условие снова захватывает свою блокировку, и метод возвращает `True`.217218Обратите внимание, что задача *может* вернуться из этого вызова ложным образом, поэтому вызывающий код должен всегда повторно проверять состояние и быть готовым снова вызвать `wait()`. По этой причине может быть предпочтительнее использовать [`wait_for()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Condition.wait_for).219220#### `async wait_for(predicate)`221222Ожидает, пока предикат не станет *истинным*.223224Предикат должен быть вызываемым объектом, результат которого интерпретируется как булево значение. Метод будет повторно вызывать [`wait()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Condition.wait) до тех пор, пока предикат не станет равным *истине*. Итоговое значение является возвращаемым.225226## Semaphore227228#### `class asyncio.Semaphore(value=1)`229230Объект семафора. Не потокобезопасен.231232Семафор управляет внутренним счётчиком, который уменьшается при каждом вызове [`acquire()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Semaphore.acquire) и увеличивается при каждом вызове [`release()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Semaphore.release). Счётчик никогда не может стать меньше нуля; когда `acquire()` обнаруживает, что он равен нулю, он блокируется в ожидании, пока какая-либо задача не вызовет `release()`.233234Необязательный аргумент *value* задаёт начальное значение внутреннего счётчика (по умолчанию `1`). Если переданное значение меньше `0`, возникает [`ValueError`](https://python-all.ru/3.16/library/exceptions.html#ValueError).235236Изменено в версии 3.10: Удалён параметр *loop*.237238Предпочтительный способ использования семафора – оператор [`async with`](https://python-all.ru/3.16/reference/compound_stmts.html#async-with):239240```python241sem = asyncio.Semaphore(10)242243# ... позже244async with sem:245 # работать с общим ресурсом246```247248это эквивалентно следующему:249250```python251sem = asyncio.Semaphore(10)252253# ... позже254await sem.acquire()255try:256 # работать с общим ресурсом257finally:258 sem.release()259```260261#### `async acquire()`262263Захватывает семафор.264265Если внутренний счётчик больше нуля, уменьшить его на единицу и немедленно вернуть `True`. Если он равен нулю, ждать, пока не будет вызван [`release()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Semaphore.release), и вернуть `True`.266267#### `locked()`268269Возвращает `True`, если семафор не может быть захвачен немедленно.270271#### `release()`272273Освобождает семафор, увеличивая внутренний счётчик на единицу. Может пробудить задачу, ожидающую захвата семафора.274275В отличие от [`BoundedSemaphore`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.BoundedSemaphore), `Semaphore` допускает больше вызовов `release()`, чем вызовов `acquire()`.276277## BoundedSemaphore278279#### `class asyncio.BoundedSemaphore(value=1)`280281Объект ограниченного семафора. Не потокобезопасен.282283Bounded Semaphore – это версия [`Semaphore`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Semaphore), которая возбуждает [`ValueError`](https://python-all.ru/3.16/library/exceptions.html#ValueError) в [`release()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Semaphore.release), если увеличивает внутренний счётчик выше начального *value*.284285Изменено в версии 3.10: Удалён параметр *loop*.286287## Barrier288289#### `class asyncio.Barrier(parties)`290291Объект барьера. Не потокобезопасен.292293Барьер – это простой примитив синхронизации, который позволяет заблокироваться, пока на нём не будет ожидать *parties* задач. Задачи могут ожидать на методе [`wait()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Barrier.wait) и будут заблокированы, пока указанное количество задач не начнут ожидать на `wait()`. В этот момент все ожидающие задачи разблокируются одновременно.294295[`async with`](https://python-all.ru/3.16/reference/compound_stmts.html#async-with) можно использовать как альтернативу ожиданию на [`wait()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Barrier.wait).296297Барьер можно использовать повторно любое количество раз.298299Пример:300301```python302async def example_barrier():303 # барьер с тремя участниками304 b = asyncio.Barrier(3)305306 # создать две новые ожидающие задачи307 asyncio.create_task(b.wait())308 asyncio.create_task(b.wait())309310 await asyncio.sleep(0)311 print(b)312313 # Третий вызов .wait() проходит барьер314 await b.wait()315 print(b)316 print("barrier passed")317318 await asyncio.sleep(0)319 print(b)320321asyncio.run(example_barrier())322```323324Результат этого примера:325326```python327<asyncio.locks.Barrier object at 0x... [filling, waiters:2/3]>328<asyncio.locks.Barrier object at 0x... [draining, waiters:0/3]>329barrier passed330<asyncio.locks.Barrier object at 0x... [filling, waiters:0/3]>331```332333Добавлено в версии 3.12.334335#### `async wait()`336337Пройти через барьер. Когда все задачи, участвующие в барьере, вызовут эту функцию, они все разблокируются одновременно.338339Когда ожидающая или заблокированная задача в барьере отменяется, эта задача покидает барьер, который остаётся в том же состоянии. Если состояние барьера – «заполнение», количество ожидающих задач уменьшается на 1.340341Возвращаемое значение – целое число от 0 до `parties-1`, различное для каждой задачи. Это можно использовать, чтобы выбрать задачу для выполнения каких-то специальных служебных действий, например:342343```python344...345async with barrier as position:346 if position == 0:347 # Только одна задача выводит это348 print('End of *draining phase*')349```350351Этот метод может вызвать исключение [`BrokenBarrierError`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.BrokenBarrierError), если барьер сломан или сброшен, пока задача ожидает. Он может вызвать [`CancelledError`](https://python-all.ru/3.16/library/asyncio-exceptions.html#asyncio.CancelledError), если задача отменена.352353#### `async reset()`354355Вернуть барьер в исходное пустое состояние. Любые задачи, ожидающие на нём, получат исключение [`BrokenBarrierError`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.BrokenBarrierError).356357Если барьер сломан, возможно, лучше просто оставить его и создать новый.358359#### `async abort()`360361Перевести барьер в сломанное состояние. Это приведёт к тому, что любые текущие или будущие вызовы [`wait()`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Barrier.wait) будут завершаться с ошибкой [`BrokenBarrierError`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.BrokenBarrierError). Используйте это, например, если одной из задач необходимо прерваться, чтобы избежать бесконечного ожидания задач.362363#### `parties`364365Количество задач, необходимых для прохождения барьера.366367#### `n_waiting`368369Количество задач, ожидающих в данный момент в барьере во время заполнения.370371#### `broken`372373Логическое значение, которое равно `True`, если барьер находится в сломанном состоянии.374375#### `exception asyncio.BrokenBarrierError`376377Это исключение, подкласс [`RuntimeError`](https://python-all.ru/3.16/library/exceptions.html#RuntimeError), вызывается, когда объект [`Barrier`](https://python-all.ru/3.16/library/asyncio-sync.html#asyncio.Barrier) сбрасывается или ломается.378379---380381Изменено в версии 3.9: Захват блокировки с помощью `await lock` или `yield from lock` и/или оператора [`with`](https://python-all.ru/3.16/reference/compound_stmts.html#with) (`with await lock`, `with (yield from lock)`) был удалён. Вместо этого используйте `async with lock`.382