У меня есть несколько сопрограмм, которые должны запускаться одновременно, некоторые из которых могут вызывать исключение. В этих случаях сопрограммы должны запускаться снова. Как это сделать? Минимальная демонстрация того, что я пытаюсь сделать:
import asyncio
import time
t = time.time()
async def c1():
print("finished c1 {}".format(time.time() - t))
async def c2():
await asyncio.sleep(3)
print("finished c2 {}".format(time.time() - t))
called = False
async def c3():
global called
# raises an exception the first time it called
if not called:
called = True
raise RuntimeError("c3 called the first time")
print("finished c3 {}".format(time.time() - t))
async def run():
pending = {c1(), c2(), c3()}
num_times_called = 0
while pending:
num_times_called += 1
print("{} times called with {} pending tasks: {}".format(num_times_called, len(pending), pending))
finished, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_EXCEPTION)
for task in finished:
if task.exception():
print("{} got an exception {}, retrying".format(task, task.exception()))
pending.add(task)
print("finished {}".format(finished))
print("finished all {}".format(time.time() - t))
asyncio.get_event_loop().run_until_complete(run())
c3()
означает, что некоторые сопрограммы не удастся и должны быть повторно запущены. Проблема с демонстрацией завершена, завершена задача и имеет набор исключений, поэтому, когда я вернусь к ожидающему набору, следующий цикл выполнения немедленно выйдет из строя без повторного запуска c3()
поскольку он уже выполнен.
Есть ли способ очистить задачу, чтобы она снова запускала c3()
? Я знаю, что экземпляр coroutine, прикрепленный к задаче, не может быть ожидаем снова, я получаю
RuntimeError('cannot reuse already awaited coroutine',)
что означает, что мне нужно вручную управлять картой из экземпляра coroutine в task._coro
который сгенерировал его, а затем извлечь неудачный экземпляр coroutine с task._coro
- это правильно?
EDIT: сама задача может быть ключом на карте, который является более чистым.
async def run():
tasks = {asyncio.ensure_future(c()): c for c in (c1, c2, c3)}
pending = set(tasks.keys())
num_times_called = 0
while pending:
num_times_called += 1
print("{} times called with {} pending tasks: {}".format(num_times_called, len(pending), pending))
finished, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_EXCEPTION)
for task in finished:
if task.exception():
print("{} got an exception {}, retrying".format(task, task.exception()))
coro = tasks[task]
new_task = asyncio.ensure_future(coro())
tasks[new_task] = coro
pending.add(new_task)
print("finished {}".format(finished))
print("finished all {}".format(time.time() - t))
coros
отображение задач сопрограммная функции:coros = {loop.create_task(c()): c for c in (c1, c2, c3)}
. Затем вы можете искать сопрограмму по заданию:coro = coros[task]
. Все остальное должно работать как написано, и код будет чистым и использовать только открытый API.