8.7. Події

asyncio.Event – найпростіший сигнальний примітив, що надається модулем. Одна корутина встановлює подію, коли щось відбулося; будь-яка кількість інших корутин чекає, поки подія буде встановлена, перш ніж продовжити. Корисного навантаження немає – подія є булевим значенням, яке вмикається і залишається ввімкненим, поки щось не скине його.

8.7.1. Базовий шаблон

import asyncio

async def waiter(evt):
    print("waiting")
    await evt.wait()
    print("got signal")

async def main():
    evt = asyncio.Event()
    asyncio.create_task(waiter(evt))
    await asyncio.sleep(1)
    evt.set()
    await asyncio.sleep(0)

asyncio.run(main())

Завдання-очікувач запускається, потрапляє на await evt.wait() і призупиняється – подія починає у скинутому стані, тому виклик wait повертає керування циклу. За секунду main викликає evt.set() і очікувач планується до відновлення. Завершальний await asyncio.sleep(0) – лише поступка, щоб цикл мав шанс запустити очікувача до того, як main повернеться.

8.7.2. Чотири методи

  • set() – встановити подію та запланувати відновлення всіх корутин, що зараз заблоковані на wait(). Подія залишається встановленою до скидання.

  • clear() – повернути подію у скинутий стан. Корутини, що виконають await wait() після цього, будуть знову заблоковані.

  • wait() – корутина. Якщо подія вже встановлена, повертається негайно. В іншому разі блокується до встановлення події.

  • is_set() – повертає поточний стан без блокування. Корисно, коли корутина хоче перевірити подію, не чекаючи на неї.

8.7.3. Кілька очікувачів

Кілька корутин можуть awaitнути одну й ту саму подію. Коли щось викликає set(), всі вони плануються до відновлення. Подія є типом «один-до-багатьох» за замовчуванням; немає потреби в кількох подіях для розгалуження до кількох очікувачів.

8.7.4. Повторне використання події

Поширений шаблон – використовувати подію повторно (корутина, що запускається раз на сигнал) – очищуючи її після кожного проходу:

async def worker(go):
    while True:
        await go.wait()
        do_one_unit_of_work()
        go.clear()

Виробник встановлює подію щоразу, коли є робота; працівник скидає її після отримання сигналу. Якщо виробник може встановити подію знову до того, як працівник прокинувся, працівник може обробити цей випадок (while go.is_set(): ...) перед скиданням.

8.7.5. Безпечні контексти

Event не є безпечним для виклику set() зсередини обробника переривань. Механізм планування очікувачів припускає, що він виконується всередині власного контексту циклу подій, чого переривання не забезпечують. Для пробудження завдання asyncio з обробника переривань правильним примітивом є ThreadSafeFlag (розглядається далі).