Чот не понимаю я ничего в #asyncio в #python, похоже. Единственный способ нормально завершать несколько запущенных asyncio.Task с while True внутри и не сыпать кучей трейсбэков при KeyboardInterrupt оказался:
asyncio.run(func)
def func():
...
await asyncio.wait([
asyncio.create_task(task1()),
asyncio.create_task(task2())
])
Любые другие приводили либо к мусору, либо к бесконечному повисанию, я явно что-то делал не так.
Что пробовал:
1. Gather
def func():
...
await asyncio.gather(
asyncio.create_task(task1()),
asyncio.create_task(task2())
)
2. Обработку сигналов и ручная отмена тасок
loop.add_signal_handler(sigint, cancel_tasks, [t1, t2]))
def cancel_all(tasks: list[Task]):
for task in tasks:
if task != asyncio.current_task() and not task.done():
task.cancel()
3. Где-то посреди этого было явное создание эвент-лупа и попытки его останавливать, что-то типа:
if __name__ == '__main__':
loop = asyncio.new_event_loop()
loop.run_until_complete(func())
try:
loop.run_forever()
except KeyboardInterrupt:
loop.stop()
4. В том числе без каких-либо зацепок на событие, то есть таски просто создавались и.. ну сдохнут и сдохнут:
def func():
...
asyncio.create_task(task1())
asyncio.create_task(task2())
Конкретно ругань уже не помню, а откатываться к падающему состоянию не хочу, но что-то про то что task was destroyed but it was pending. Типа надо дождаться пока они на следующих тиках эвентлупа нормально отработают asyncio.CancelledError и только после этого уже loop стопать. Но мне чот не повезло и дебаггером побегать по коду обработки сигнала не вышло, он тупо завершался, вот я хер и забил.
Теперь чувствую себя джуном :c
@strizhechenko «просто создавать таски» из 4 пункта нельзя.
За ними может прийти GC и они испарятся.
Что у тебя и происходит, вроде как.
Вариант с gather наиболее логичный и он должен работать.
Проверь параметры gather’а, он умеет обрабатывать исключения.
Если совсем правильно делать, конечно, то надо в каждой таске ловить и обрабатывать cancelerror, ну и кэнселить их.
Плюс у тебя await’ы в некоторых местах в синхронных функах, это точно то?)
@strizhechenko «просто создавать таски» из 4 пункта нельзя.
За ними может прийти GC и они испарятся.
Что у тебя и происходит, вроде как.
Вариант с gather наиболее логичный и он должен работать.
Проверь параметры gather’а, он умеет обрабатывать исключения.
Если совсем правильно делать, конечно, то надо в каждой таске ловить и обрабатывать cancelerror, ну и кэнселить их.