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