> **Источник:** https://python-all.ru/3.11/library/asyncio-sync.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

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

**Исходный код:** [Lib/asyncio/locks.py](https://python-all.ru/src/3.11/Lib/asyncio/locks.py)

---

Примитивы синхронизации asyncio устроены аналогично примитивам модуля [`threading`](https://python-all.ru/3.11/library/threading.html#module-threading), но с двумя важными оговорками:

- Примитивы asyncio не являются потокобезопасными, поэтому их не следует использовать для синхронизации потоков ОС (для этого используйте [`threading`](https://python-all.ru/3.11/library/threading.html#module-threading));
- Методы этих примитивов синхронизации не принимают аргумент *timeout*; для выполнения операций с таймаутами используйте функцию [`asyncio.wait_for()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.wait_for).

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

- [`Lock`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Lock)
- [`Event`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Event)
- [`Condition`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Condition)
- [`Semaphore`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Semaphore)
- [`BoundedSemaphore`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.BoundedSemaphore)
- [`Barrier`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Barrier)

---

## Блокировка

#### `class asyncio.Lock`

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

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

The preferred way to use a Lock is an [`async with`](https://python-all.ru/3.11/reference/compound_stmts.html#async-with) statement:

```python
lock = asyncio.Lock()

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

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

```python
lock = asyncio.Lock()

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

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

#### `coroutine acquire()`

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

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

Если несколько корутин заблокированы вызовом [`acquire()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Lock.acquire) в ожидании разблокировки блокировки, только одна из них в итоге продолжит выполнение.

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

#### `release()`

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

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

Если блокировка *разблокирована*, возбуждается исключение [`RuntimeError`](https://python-all.ru/3.11/library/exceptions.html#RuntimeError).

#### `locked()`

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

## Event

#### `class asyncio.Event`

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

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

Объект Event управляет внутренним флагом, который может быть установлен в *true* методом [`set()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Event.set) и сброшен в *false* методом [`clear()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Event.clear). Метод [`wait()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Event.wait) блокируется, пока флаг не будет установлен в *true*. Изначально флаг установлен в *false*.

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

Пример:

```python
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()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Event.set).

#### `set()`

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

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

#### `clear()`

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

Задачи, ожидающие на [`wait()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Event.wait), теперь будут блокироваться, пока метод [`set()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Event.set) не будет вызван снова.

#### `is_set()`

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

## Condition

#### `class asyncio.Condition(lock=None)`

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

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

По сути, объект Condition объединяет функциональность [`Event`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Event) и [`Lock`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Lock). Несколько объектов Condition могут совместно использовать одну блокировку, что позволяет координировать исключительный доступ к общему ресурсу между различными задачами, заинтересованными в определённых состояниях этого ресурса.

Необязательный аргумент *блокировка* должен быть объектом [`Lock`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Lock) или `None`. В последнем случае новый объект блокировки создаётся автоматически.

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

Предпочтительный способ использования Condition – оператор [`async with`](https://python-all.ru/3.11/reference/compound_stmts.html#async-with):

```python
cond = asyncio.Condition()

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

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

```python
cond = asyncio.Condition()

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

#### `coroutine acquire()`

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

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

#### `notify(n=1)`

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

Блокировка должна быть захвачена до вызова этого метода и освобождена сразу после. Если вызвать с *разблокированной* блокировкой, будет возбуждено исключение [`RuntimeError`](https://python-all.ru/3.11/library/exceptions.html#RuntimeError).

#### `locked()`

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

#### `notify_all()`

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

Этот метод действует как [`notify()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Condition.notify), но пробуждает все ожидающие задачи.

Блокировка должна быть захвачена до вызова этого метода и освобождена сразу после. Если вызвать с *разблокированной* блокировкой, будет возбуждено исключение [`RuntimeError`](https://python-all.ru/3.11/library/exceptions.html#RuntimeError).

#### `release()`

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

При вызове на незаблокированной блокировке возникает [`RuntimeError`](https://python-all.ru/3.11/library/exceptions.html#RuntimeError).

#### `coroutine wait()`

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

Если вызывающая задача не захватила блокировку на момент вызова этого метода, возникает [`RuntimeError`](https://python-all.ru/3.11/library/exceptions.html#RuntimeError).

Этот метод освобождает базовую блокировку и блокируется до тех пор, пока не будет пробуждён вызовом [`notify()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Condition.notify) или [`notify_all()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Condition.notify_all). После пробуждения условие снова захватывает свою блокировку, и метод возвращает `True`.

#### `coroutine wait_for(predicate)`

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

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

## Semaphore

#### `class asyncio.Semaphore(value=1)`

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

Семафор управляет внутренним счётчиком, который уменьшается при каждом вызове [`acquire()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Semaphore.acquire) и увеличивается при каждом вызове [`release()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Semaphore.release). Счётчик никогда не может стать меньше нуля; когда [`acquire()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Semaphore.acquire) обнаруживает, что он равен нулю, он блокируется в ожидании, пока какая-либо задача не вызовет [`release()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Semaphore.release).

Необязательный аргумент *value* задаёт начальное значение внутреннего счётчика (по умолчанию `1`). Если переданное значение меньше `0`, возникает [`ValueError`](https://python-all.ru/3.11/library/exceptions.html#ValueError).

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

Предпочтительный способ использования семафора – оператор [`async with`](https://python-all.ru/3.11/reference/compound_stmts.html#async-with):

```python
sem = asyncio.Semaphore(10)

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

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

```python
sem = asyncio.Semaphore(10)

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

#### `coroutine acquire()`

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

Если внутренний счётчик больше нуля, уменьшить его на единицу и немедленно вернуть `True`. Если он равен нулю, ждать, пока не будет вызван [`release()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Semaphore.release), и вернуть `True`.

#### `locked()`

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

#### `release()`

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

В отличие от [`BoundedSemaphore`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.BoundedSemaphore), [`Semaphore`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Semaphore) допускает больше вызовов `release()`, чем вызовов `acquire()`.

## BoundedSemaphore

#### `class asyncio.BoundedSemaphore(value=1)`

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

Bounded Semaphore – это версия [`Semaphore`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Semaphore), которая возбуждает [`ValueError`](https://python-all.ru/3.11/library/exceptions.html#ValueError) в [`release()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Semaphore.release), если увеличивает внутренний счётчик выше начального *value*.

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

## Barrier

#### `class asyncio.Barrier(parties)`

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

Барьер – это простой примитив синхронизации, который позволяет заблокироваться, пока на нём не будет ожидать *parties* задач. Задачи могут ожидать на методе [`wait()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Barrier.wait) и будут заблокированы, пока указанное количество задач не начнут ожидать на [`wait()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Barrier.wait). В этот момент все ожидающие задачи разблокируются одновременно.

[`async with`](https://python-all.ru/3.11/reference/compound_stmts.html#async-with) можно использовать как альтернативу ожиданию на [`wait()`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Barrier.wait).

Барьер можно использовать повторно любое количество раз.

Пример:

```python
async def example_barrier():
   # барьер с тремя участниками
   b = asyncio.Barrier(3)

   # создать две новые ожидающие задачи
   asyncio.create_task(b.wait())
   asyncio.create_task(b.wait())

   await asyncio.sleep(0)
   print(b)

   # Третий вызов .wait() проходит барьер
   await b.wait()
   print(b)
   print("barrier passed")

   await asyncio.sleep(0)
   print(b)

asyncio.run(example_barrier())
```

Результат этого примера:

```python
<asyncio.locks.Barrier object at 0x... [filling, waiters:2/3]>
<asyncio.locks.Barrier object at 0x... [draining, waiters:0/3]>
barrier passed
<asyncio.locks.Barrier object at 0x... [filling, waiters:0/3]>
```

Новое в версии 3.11.

#### `coroutine wait()`

Пройти через барьер. Когда все задачи, участвующие в барьере, вызовут эту функцию, они все разблокируются одновременно.

Когда ожидающая или заблокированная задача в барьере отменяется, эта задача покидает барьер, который остаётся в том же состоянии. Если состояние барьера – «заполнение», количество ожидающих задач уменьшается на 1.

Возвращаемое значение – целое число от 0 до `parties-1`, различное для каждой задачи. Это можно использовать, чтобы выбрать задачу для выполнения каких-то специальных служебных действий, например:

```python
...
async with barrier as position:
   if position == 0:
      # Только одна задача выводит это
      print('End of *draining phase*')
```

Этот метод может вызвать исключение [`BrokenBarrierError`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.BrokenBarrierError), если барьер сломан или сброшен, пока задача ожидает. Он может вызвать [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError), если задача отменена.

#### `coroutine reset()`

Вернуть барьер в исходное пустое состояние. Любые задачи, ожидающие на нём, получат исключение [`BrokenBarrierError`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.BrokenBarrierError).

Если барьер сломан, возможно, лучше просто оставить его и создать новый.

#### `coroutine abort()`

Перевести барьер в сломанное состояние. Это приведёт к тому, что любые текущие или будущие вызовы [`wait()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.wait) будут завершаться с ошибкой [`BrokenBarrierError`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.BrokenBarrierError). Используйте это, например, если одной из задач необходимо прерваться, чтобы избежать бесконечного ожидания задач.

#### `parties`

Количество задач, необходимых для прохождения барьера.

#### `n_waiting`

Количество задач, ожидающих в данный момент в барьере во время заполнения.

#### `broken`

Логическое значение, которое равно `True`, если барьер находится в сломанном состоянии.

#### `exception asyncio.BrokenBarrierError`

Это исключение, подкласс [`RuntimeError`](https://python-all.ru/3.11/library/exceptions.html#RuntimeError), вызывается, когда объект [`Barrier`](https://python-all.ru/3.11/library/asyncio-sync.html#asyncio.Barrier) сбрасывается или ломается.

---

Изменено в версии 3.9: Захват блокировки с помощью `await lock` или `yield from lock` и/или оператора [`with`](https://python-all.ru/3.11/reference/compound_stmts.html#with) (`with await lock`, `with (yield from lock)`) был удалён. Вместо этого используйте `async with lock`.
