asyncio 的 coroutine对象 与 Future对象使用指南

 更新时间:2016年09月11日 10:00:26   投稿:hebedich  
asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。asyncio的编程模型就是一个消息循环。今天我们就来详细讨论下asyncio 中的 coroutine 与 Future对象

coroutine 与 Future 的关系

看起来两者是一样的,因为都可以用以下的语法来异步获取结果,

result = await future
result = await coroutine

实际上,coroutine 是生成器函数,它既可以从外部接受参数,也可以产生结果。使用 coroutine 的好处是,我们可以暂停一个函数,然后稍后恢复执行。比如在涉及到网路操作的情况下,能够停下函数直到响应到来。在停下的这段时间内,我们可以切换到其他任务继续执行。

而 Future 更像是 Javascript 中的 Promise 对象。它是一个占位符,其值会在将来被计算出来。在上述的例子中,当我们在等待网络 IO 函数完成时,函数会给我们一个容器,Promise 会在完成时填充该容器。填充完毕后,我们可以用回调函数来获取实际结果。

Task 对象是 Future 的子类,它将 coroutine 和 Future 联系在一起,将 coroutine 封装成一个 Future 对象。

一般会看到两种任务启动方法,

tasks = asyncio.gather(
  asyncio.ensure_future(func1()),
  asyncio.ensure_future(func2())
)
loop.run_until_complete(tasks)


tasks = [
  asyncio.ensure_future(func1()),
  asyncio.ensure_future(func2())
  ]
loop.run_until_complete(asyncio.wait(tasks))

ensure_future 可以将 coroutine 封装成 Task。asyncio.gather 将一些 Future 和 coroutine 封装成一个 Future。

asyncio.wait 则本身就是 coroutine。

run_until_complete 既可以接收 Future 对象,也可以是 coroutine 对象,

BaseEventLoop.run_until_complete(future)

Run until the Future is done.
If the argument is a coroutine object, it is wrapped by ensure_future().
Return the Future's result, or raise its exception.

Task 任务的正确退出方式

在 asyncio 的任务循环中,如果使用 CTRL-C 退出的话,即使捕获了异常,Event Loop 中的任务会报错,出现如下的错误,

Task was destroyed but it is pending!
task: <Task pending coro=<kill_me() done, defined at test.py:5> wait_for=<Future pending cb=[Task._wakeup()]>>

根据官方文档,Task 对象只有在以下几种情况,会认为是退出,

a result / exception are available, or that the future was cancelled

Task 对象的 cancel 和其父类 Future 略有不同。当调用 Task.cancel() 后,对应 coroutine 会在事件循环的下一轮中抛出 CancelledError 异常。使用 Future.cancelled() 并不能立即返回 True(用来表示任务结束),只有在上述异常被处理任务结束后才算是 cancelled。

故结束任务可以用

for task in asyncio.Task.all_tasks():
  task.cancel()

这种方法将所有任务找出并 cancel。

但 CTRL-C 也会将事件循环停止,所以有必要重启事件循环,

try:
  loop.run_until_complete(tasks)
except KeyboardInterrupt as e:
  for task in asyncio.Task.all_tasks():
    task.cancel()
  loop.run_forever() # restart loop
finally:
  loop.close()

在每个 Task 中捕获异常是必要的,如果不确定,可以使用

asyncio.gather(..., return_exceptions=True)

将异常转换为正常的结果返回。

相关文章

  • 使用Python将PDF文件转存为图片的代码示例

    使用Python将PDF文件转存为图片的代码示例

    因工作中的某些奇葩要求,需要将PDF文件的每页内容转存成按顺序编号的图片,用第三方软件或者在线转换也可以,但批量操作还是Python方便,所以本文给大家介绍了使用Python将PDF文件转存为图片的方法,需要的朋友可以参考下
    2023-09-09
  • 10个Python常用的损失函数及代码实现分享

    10个Python常用的损失函数及代码实现分享

    损失函数是一种衡量模型与数据吻合程度的算法。损失函数测量实际测量值和预测值之间差距的一种方式。本文为大家总结了10个常用的损失函数及Python代码实现,需要的可以参考一下
    2022-09-09
  • python selenium UI自动化解决验证码的4种方法

    python selenium UI自动化解决验证码的4种方法

    本篇文章主要介绍了python selenium UI自动化解决验证码的4种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • Python编写的com组件发生R6034错误的原因与解决办法

    Python编写的com组件发生R6034错误的原因与解决办法

    pythoncom27.dll可能没有包含manifest信息,或者没有包含正确的manifest信息,或者系统中的c++ runtime library受到破坏都有可能造成这种现象
    2013-04-04
  • Python3.4学习笔记之 idle 清屏扩展插件用法分析

    Python3.4学习笔记之 idle 清屏扩展插件用法分析

    这篇文章主要介绍了Python3.4 idle 清屏扩展插件用法,简单分析了idle清屏的几种方法及idle清屏插件的相关使用技巧,需要的朋友可以参考下
    2019-03-03
  • caffe的python接口生成配置文件学习

    caffe的python接口生成配置文件学习

    这篇文章主要介绍了caffe的python接口生成配置文件学习,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Python中字符串格式化的方法详解

    Python中字符串格式化的方法详解

    众所周知,我们可以使用 print() 方法来输出一个固定内容的字符串,就像是 print("Hello, world!") 一样,但是,在现实中,我们很多时候是需要输出一些内容不断变化,或者内容动态生成的字符串,本文将介绍如何按照需要,在Python中格式化一个字符串
    2023-10-10
  • 使用Python实现FTP文件自动传输脚本

    使用Python实现FTP文件自动传输脚本

    这篇文章主要为大家详细介绍了如何使用Python实现FTP文件自动传输脚本,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以了解下
    2023-12-12
  • python多线程调用exit无法退出的解决方法

    python多线程调用exit无法退出的解决方法

    今天小编就为大家分享一篇python多线程调用exit无法退出的解决方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-02-02
  • python基于Tkinter库实现简单文本编辑器实例

    python基于Tkinter库实现简单文本编辑器实例

    这篇文章主要介绍了python基于Tkinter库实现简单文本编辑器,实例分析了Python使用Tkinter库实现简单桌面应用程序的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-05-05

最新评论