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 (розглядається далі).