asyncio-sync.md
1> **Источник:** https://python-all.ru/3.12/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/3.12/Lib/asyncio/locks.py)1011---1213Примитивы синхронизации asyncio устроены аналогично примитивам модуля [`threading`](https://python-all.ru/3.12/library/threading.html#module-threading), но с двумя важными оговорками:1415- Примитивы asyncio не являются потокобезопасными, поэтому их не следует использовать для синхронизации потоков ОС (для этого используйте [`threading`](https://python-all.ru/3.12/library/threading.html#module-threading));16- Методы этих примитивов синхронизации не принимают аргумент *timeout*; для выполнения операций с таймаутами используйте функцию [`asyncio.wait_for()`](https://python-all.ru/3.12/library/asyncio-task.html#asyncio.wait_for).1718В asyncio имеются следующие базовые примитивы синхронизации:1920- [`Lock`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Lock)21- [`Event`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Event)22- [`Condition`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Condition)23- [`Semaphore`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Semaphore)24- [`BoundedSemaphore`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.BoundedSemaphore)25- [`Barrier`](https://python-all.ru/3.12/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.12/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()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Lock.acquire) в ожидании разблокировки блокировки, только одна из них в итоге продолжит выполнение.6970Захват блокировки *справедлив*: выполнение продолжит первая корутина, начавшая ожидание блокировки.7172#### `release()`7374Освобождает блокировку.7576Если блокировка *заблокирована*, сбрасывает её в состояние *разблокирована* и завершается.7778Если блокировка *разблокирована*, возбуждается исключение [`RuntimeError`](https://python-all.ru/3.12/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.12/library/asyncio-sync.html#asyncio.Event.set) и сброшен в *false* методом [`clear()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Event.clear). Метод [`wait()`](https://python-all.ru/3.12/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.12/library/asyncio-sync.html#asyncio.Event.set).126127#### `set()`128129Устанавливает событие.130131Все задачи, ожидающие установки события, будут немедленно пробуждены.132133#### `clear()`134135Сбрасывает (снимает) событие.136137Задачи, ожидающие на [`wait()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Event.wait), теперь будут блокироваться, пока метод [`set()`](https://python-all.ru/3.12/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.12/library/asyncio-sync.html#asyncio.Event) и [`Lock`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Lock). Несколько объектов Condition могут совместно использовать одну блокировку, что позволяет координировать исключительный доступ к общему ресурсу между различными задачами, заинтересованными в определённых состояниях этого ресурса.152153Необязательный аргумент *блокировка* должен быть объектом [`Lock`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Lock) или `None`. В последнем случае новый объект блокировки создаётся автоматически.154155Изменено в версии 3.10: Удалён параметр *loop*.156157Предпочтительный способ использования Condition – оператор [`async with`](https://python-all.ru/3.12/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), ожидающих на этом условии. Если ожидающих задач нет, метод ничего не делает.189190Блокировка должна быть захвачена до вызова этого метода и освобождена сразу после. Если вызвать с *разблокированной* блокировкой, будет возбуждено исключение [`RuntimeError`](https://python-all.ru/3.12/library/exceptions.html#RuntimeError).191192#### `locked()`193194Возвращает `True`, если базовая блокировка захвачена.195196#### `notify_all()`197198Пробуждает все задачи, ожидающие данное условие.199200Этот метод действует как [`notify()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Condition.notify), но пробуждает все ожидающие задачи.201202Блокировка должна быть захвачена до вызова этого метода и освобождена сразу после. Если вызвать с *разблокированной* блокировкой, будет возбуждено исключение [`RuntimeError`](https://python-all.ru/3.12/library/exceptions.html#RuntimeError).203204#### `release()`205206Освобождает базовую блокировку.207208При вызове на незаблокированной блокировке возникает [`RuntimeError`](https://python-all.ru/3.12/library/exceptions.html#RuntimeError).209210#### `async wait()`211212Ожидает уведомления.213214Если вызывающая задача не захватила блокировку на момент вызова этого метода, возникает [`RuntimeError`](https://python-all.ru/3.12/library/exceptions.html#RuntimeError).215216Этот метод освобождает базовую блокировку и блокируется до тех пор, пока не будет пробуждён вызовом [`notify()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Condition.notify) или [`notify_all()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Condition.notify_all). После пробуждения условие снова захватывает свою блокировку, и метод возвращает `True`.217218#### `async wait_for(predicate)`219220Ожидает, пока предикат не станет *истинным*.221222Предикат должен быть вызываемым объектом, результат которого будет интерпретироваться как логическое значение. Метод будет повторно [`wait()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Condition.wait), пока предикат не примет значение *true*. Результат последнего вызова будет возвращён.223224## Semaphore225226#### `class asyncio.Semaphore(value=1)`227228Объект семафора. Не потокобезопасен.229230Семафор управляет внутренним счётчиком, который уменьшается при каждом вызове [`acquire()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Semaphore.acquire) и увеличивается при каждом вызове [`release()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Semaphore.release). Счётчик никогда не может стать меньше нуля; когда [`acquire()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Semaphore.acquire) обнаруживает, что он равен нулю, он блокируется в ожидании, пока какая-либо задача не вызовет [`release()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Semaphore.release).231232Необязательный аргумент *value* задаёт начальное значение внутреннего счётчика (по умолчанию `1`). Если переданное значение меньше `0`, возникает [`ValueError`](https://python-all.ru/3.12/library/exceptions.html#ValueError).233234Изменено в версии 3.10: Удалён параметр *loop*.235236Предпочтительный способ использования семафора – оператор [`async with`](https://python-all.ru/3.12/reference/compound_stmts.html#async-with):237238```python239sem = asyncio.Semaphore(10)240241# ... позже242async with sem:243 # работать с общим ресурсом244```245246это эквивалентно следующему:247248```python249sem = asyncio.Semaphore(10)250251# ... позже252await sem.acquire()253try:254 # работать с общим ресурсом255finally:256 sem.release()257```258259#### `async acquire()`260261Захватывает семафор.262263Если внутренний счётчик больше нуля, уменьшить его на единицу и немедленно вернуть `True`. Если он равен нулю, ждать, пока не будет вызван [`release()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Semaphore.release), и вернуть `True`.264265#### `locked()`266267Возвращает `True`, если семафор не может быть захвачен немедленно.268269#### `release()`270271Освобождает семафор, увеличивая внутренний счётчик на единицу. Может пробудить задачу, ожидающую захвата семафора.272273В отличие от [`BoundedSemaphore`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.BoundedSemaphore), [`Semaphore`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Semaphore) допускает больше вызовов `release()`, чем вызовов `acquire()`.274275## BoundedSemaphore276277#### `class asyncio.BoundedSemaphore(value=1)`278279Объект ограниченного семафора. Не потокобезопасен.280281Bounded Semaphore – это версия [`Semaphore`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Semaphore), которая возбуждает [`ValueError`](https://python-all.ru/3.12/library/exceptions.html#ValueError) в [`release()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Semaphore.release), если увеличивает внутренний счётчик выше начального *value*.282283Изменено в версии 3.10: Удалён параметр *loop*.284285## Barrier286287#### `class asyncio.Barrier(parties)`288289Объект барьера. Не потокобезопасен.290291Барьер – это простой примитив синхронизации, который позволяет заблокироваться, пока на нём не будет ожидать *parties* задач. Задачи могут ожидать на методе [`wait()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Barrier.wait) и будут заблокированы, пока указанное количество задач не начнут ожидать на [`wait()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Barrier.wait). В этот момент все ожидающие задачи разблокируются одновременно.292293[`async with`](https://python-all.ru/3.12/reference/compound_stmts.html#async-with) можно использовать как альтернативу ожиданию на [`wait()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Barrier.wait).294295Барьер можно использовать повторно любое количество раз.296297Пример:298299```python300async def example_barrier():301 # барьер с тремя участниками302 b = asyncio.Barrier(3)303304 # создать две новые ожидающие задачи305 asyncio.create_task(b.wait())306 asyncio.create_task(b.wait())307308 await asyncio.sleep(0)309 print(b)310311 # Третий вызов .wait() проходит барьер312 await b.wait()313 print(b)314 print("barrier passed")315316 await asyncio.sleep(0)317 print(b)318319asyncio.run(example_barrier())320```321322Результат этого примера:323324```python325<asyncio.locks.Barrier object at 0x... [filling, waiters:2/3]>326<asyncio.locks.Barrier object at 0x... [draining, waiters:0/3]>327barrier passed328<asyncio.locks.Barrier object at 0x... [filling, waiters:0/3]>329```330331Добавлено в версии 3.12.332333#### `async wait()`334335Пройти через барьер. Когда все задачи, участвующие в барьере, вызовут эту функцию, они все разблокируются одновременно.336337Когда ожидающая или заблокированная задача в барьере отменяется, эта задача покидает барьер, который остаётся в том же состоянии. Если состояние барьера – «заполнение», количество ожидающих задач уменьшается на 1.338339Возвращаемое значение – целое число от 0 до `parties-1`, различное для каждой задачи. Это можно использовать, чтобы выбрать задачу для выполнения каких-то специальных служебных действий, например:340341```python342...343async with barrier as position:344 if position == 0:345 # Только одна задача выводит это346 print('End of *draining phase*')347```348349Этот метод может вызвать исключение [`BrokenBarrierError`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.BrokenBarrierError), если барьер сломан или сброшен, пока задача ожидает. Он может вызвать [`CancelledError`](https://python-all.ru/3.12/library/asyncio-exceptions.html#asyncio.CancelledError), если задача отменена.350351#### `async reset()`352353Вернуть барьер в исходное пустое состояние. Любые задачи, ожидающие на нём, получат исключение [`BrokenBarrierError`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.BrokenBarrierError).354355Если барьер сломан, возможно, лучше просто оставить его и создать новый.356357#### `async abort()`358359Перевести барьер в сломанное состояние. Это приведёт к тому, что любые текущие или будущие вызовы [`wait()`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Barrier.wait) будут завершаться с ошибкой [`BrokenBarrierError`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.BrokenBarrierError). Используйте это, например, если одной из задач необходимо прерваться, чтобы избежать бесконечного ожидания задач.360361#### `parties`362363Количество задач, необходимых для прохождения барьера.364365#### `n_waiting`366367Количество задач, ожидающих в данный момент в барьере во время заполнения.368369#### `broken`370371Логическое значение, которое равно `True`, если барьер находится в сломанном состоянии.372373#### `exception asyncio.BrokenBarrierError`374375Это исключение, подкласс [`RuntimeError`](https://python-all.ru/3.12/library/exceptions.html#RuntimeError), вызывается, когда объект [`Barrier`](https://python-all.ru/3.12/library/asyncio-sync.html#asyncio.Barrier) сбрасывается или ломается.376377---378379Изменено в версии 3.9: Захват блокировки с помощью `await lock` или `yield from lock` и/или оператора [`with`](https://python-all.ru/3.12/reference/compound_stmts.html#with) (`with await lock`, `with (yield from lock)`) был удалён. Вместо этого используйте `async with lock`.380