asyncio.CancelledError не обнаружен - PullRequest
       8

asyncio.CancelledError не обнаружен

0 голосов
/ 26 августа 2018

Почему CancelledError не пойман в этом примере?

import asyncio

q = asyncio.Queue()

async def getter():
    try:
        v = await q.get()
        print(f"getter got {v}")
    except asyncio.CancelledError:
        print("getter cancelled")

async def test():
    task = asyncio.ensure_future(getter())
    task.cancel()
    await task

def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(test())

if __name__ == '__main__':
    main()

Я ожидал получить сообщение "getter отменен", но вместо этого получил трассировку стека:

Traceback (most recent call last):
  File "ce.py", line 22, in 
    main()
  File "ce.py", line 19, in main
    loop.run_until_complete(test())
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 468, in run_until_complete
    return future.result()
concurrent.futures._base.CancelledError

Task.cancel состояния:

Это позволяет отменить ошибку CancelledError сопрограмма на следующий цикл через цикл событий. Сопрограмма тогда имеет возможность очистить или даже отклонить запрос, используя попробовать / за исключением /, наконец.

1 Ответ

0 голосов
/ 26 августа 2018

Проблема в том, что getter даже не не запускает , что можно подтвердить, добавив печать в начале. Поскольку блок try никогда не вводился, except также не запускался.

Это происходит потому, что, в отличие от await, ensure_future не запускает выполнение сопрограммы сразу, а просто планирует ее запуск на следующей итерации цикла событий, как call_soon для обычных функций. Поскольку вы немедленно отменяете задачу, она удаляется из набора, который можно запустить, и ее сопрограмма закрывается, даже не начавшись.

Добавьте await asyncio.sleep(0) перед task.cancel(), и вы должны наблюдать за ожидаемым поведением. Я подозреваю, что вам не нужно вносить такие изменения в ваш реальный код - в маловероятном случае, если задача будет отменена до ее запуска, как в примере, у нее не будет возможности получить ресурсы, которые пытаются / кроме очищает на первом месте.

Два тангенциальных замечания:

  • Вы, вероятно, захотите повторно повысить asyncio.CancelledError после обработки, иначе это будет подавлено. Это не проблема в getter, как показано в вопросе, но это может быть проблемой, если код был похоронен в вызове функции. Еще лучше рассмотреть возможность использования finally или with, которые распространяют исключение и обеспечивают освобождение ресурсов независимо от типа исключения.

  • Когда вам нужно создать задачу и запустить сопрограмму, loop.create_task - это , предпочтительнее - asyncio.ensure_future. Короче говоря, хотя оба делают одно и то же для сопрограмм, create_task проясняет намерение; ensure_future предназначен для приема более широкого спектра объектов и обеспечения будущего неопределенного типа.

...