python3 的 asyncio 是一个功能强大的异步协程库,然后,复杂性导致学习曲线非常陡峭。与c#相比 async/await,python3 的界面 asyncio冗长且难以使用。这里总结一下使用时常见错误
常见错误一
# python 异常RuntimeWarning: coroutine foo was never awaited
此警告可能在许多情况下发生,但是原因是相同的:
协程对象是通过调用 async 函数创建的,但是记录不会插入到 EventLoop
考虑以下 async 函数
aysnc def foo():# do something# 没有返回值
如果要 foo() 作为异步任务调用,并且不关心结果:
prepare_for_foo()foo() # RuntimeWarning: coroutine foo was never awaitedremaining_work_not_depends_on_foo()
这是因为调用 foo() 实际上并不运行该函数,而是创建了一个“协程对象”。当当前EventLoop有机会时,将执行此协程对象: awaited/yield from 被调用或者所有先前的任务都已经完成
为了不执行异步任务 await,使用 loop.create_task() 具有 loop.run_until_complete()
prepare_for_foo()task = loop.create_task(foo())remaining_work_not_depends_on_foo()loop.run_until_completa(task)
如果创建了 coroutine 对象并将其插入到 EventLoop 中,但是从未完成,则会出现下一个警告
常见错误二
# python异常Task was destroyed but it is pending!
造成此问题的原因是 EventLoop取消待片蝗任务暂不关闭权限。 因为 Task.cancel() 安排 CancelledError在下一个循环中通过事件循环将a抛入包装的协同程序,并且协同程序然后有机会使用 try/except/finally 清理甚至拒绝请求
要正确取消所有任务并关闭 EventLoop,EventLoop应该给予最后机会运行所有已经取消但未完成的任务
合,这是取消所有任务的代码
def cancel_task():# get all task in current looptasks = Task.all_tasks()for t in tasks:t.cancel()
下面的代码正确处理任务取消和清理。 它通过调用loop.run_forever()启动EventLoop,并在接收到loop.stop()后清除任务:
try:loop.run_forever() # run_forever() returns after calling loop.stop()tasks = Task.all_tasks()for t in [t for t in tasks if not (t.done() or t.cancelled())]:# give canceled tasks the last chance to runloop.run_until_complete(t)finally:loop.close()
常见错误三
Task/Future is awaited in a different EventLoop than it is created
对于熟悉C#async / await的人来说,这个错误尤其令人惊讶。 这是因为大部分asyncio都不是线程安全的,也不是asyncio.Future或asyncio.Task。 另外,不要将asyncio.Future与concurrent.futures.Future混淆,因为它们不兼容(至少在Python 3.6之前):后者是线程安全的,而前者不是。
为了在不同的线程中等待asyncio.Future,asyncio.Future可以包装在concurrent.Future中:
def wrap_future(asyncio_future):def done_callback(af, cf):try:cf.set_result(af.result())except Exception as e:acf.set_exception(e)concur_future = concurrent.Future()asyncio_future.add_done_callback(lambda f: done_callback(f, cf=concur_future))return concur_future
参考
https://xinhuang.github.io/posts/2017-07-31-common-mistakes-using-python3-asyncio.html
