#python #asyncio
В питоновой асинхронщине есть asyncio.Event, который позволяет уведомить соседний таск о каком-нибудь событии: import asyncio event = asyncio.Event() async def task1(): event.set() async def task2(): while True: await event.wait() event.clear() # do_some_work() Однако для тех же задач можно попробовать использовать и просто asyncio.Future: import asyncio event = asyncio.Future() async def task1(): if not event.done(): event.set_result(True) async def task2(): global event while True: await event event = asyncio.Future() # clear # do_some_work() Эти примеры на первый взгляд эквивалентны и работают одинаково. Но, заглянув в реализацию threading.Event, я увидел, что там используется несколько футур в self._waiters = collections.deque(), и для каждого ожидающего создаётся своя отдельная футура. Однако ведь вроде можно await'ить одну и ту же футуру несколько раз. Значит, для всех ожидающих достаточно одной футуры. Или нельзя? Короче, зачем asyncio.Event устроен так сложно? Зачем в нём deque и несколько футур? На какие грабли я рискую напороться, если вместо asyncio.Event буду использовать одну-единственную футуру, как во втором примере?
Ответы
Ответ 1
В своих экспериментах я забыл про отмену тасков. Если запустить task2 из второго примера несколько раз, а потом один из этих task2 отменить: t = asyncio.create_task(task2()) await asyncio.sleep(1) t.cancel() ...то футура перейдёт в состояние cancelled, и все остальные экземпляры task2 тоже отменятся, что скорее всего является нежелательным поведением. Если делать для каждого ожидающего свою отдельную футуру, то эти отдельные футуры могут без проблем отменяться, не мешая всем остальным ожидающим. Поэтому в asyncio.Event и нужен self._waiters, хранящий по футуре для каждого ожидающего. Но это всё актуально только в случае, если ожидающих несколько. Если ожидающий таск всегда один, то, видимо, разницы между этими двумя примерами всё-таки нет.
Комментариев нет:
Отправить комментарий