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 awaited
remaining_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 loop
tasks = 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 run
loop.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