asyncio介绍

熟悉c#的同学可能知道,在c#中可以很方便的使用async和await来实现异步编程,那么在python中应该怎么做呢,其实python也支持异步编程,一般使用asyncio这个库,下面介绍下什么是asyncio:

asyncio 是用来编写 并发 代码的库,使用async/await语法。

asyncio 被用作多个提供高性能Python异步框架的基础,包括网络和网站服务,数据库连接库,分布式任务队列等等。

asyncio 往往是构建 IO 密集型和高层级 结构化 网络代码的最佳选择。

asyncio中的基本概念

可以看见,使用asyncio库我们也可以在python代码中使用async和await。在asyncio中,有四个基本概念,分别是:

Eventloop

Eventloop可以说是asyncio应用的核心,中央总控,Eventloop实例提供了注册、取消、执行任务和回调 的方法。

简单来说,就是我们可以把一些异步函数注册到这个事件循环上,事件循环回循环执行这些函数(每次只能执行一个),如果当前正在执行的函数在等待I/O返回,那么事件循环就会暂停它的执行去执行其他函数。当某个函数完成I/O后会恢复,等到下次循环到它的时候就会继续执行。

Coroutine

协程本质就是一个函数,

import asyncio

import time

async def a():

print('Suspending a')

await asyncio.sleep(3)

print('Resuming a')

async def b():

print('Suspending b')

await asyncio.sleep(1)

print('Resuming b')

async def main():

start = time.perf_counter()

await asyncio.gather(a(), b())

print(f'{main.__name__}Cost:{time.perf_counter() - start}')

if __name__ == '__main__':

asyncio.run(main())

执行上述代码,可以看到类似这样的输出:

Suspending a

Suspending b

Resuming b

Resuming a

main Cost: 3.0023356619999997

关于协程的具体介绍,可以参考我以前的文章 python中的协程 不过以前的那种写法,需要使用装饰器,已经过时了。

Future

Future是表示一个“未来”对象,类似于javascript中的promise,当异步操作结束后会把最终结果设置到这个Future对象上,Future是对协程的封装。

>>> import asyncio

>>> def fun():

... print("inner fun")

... return 111

...

>>> loop = asyncio.get_event_loop()

>>> future = loop.run_in_executor(None, fun) #这里没有使用await

inner fun

>>> future #可以看到,fun方法状态是pending

._call_check_cancel() at /usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/futures.py:348]>

>>> future.done() # 还没有完成

False

>>> [m for m in dir(future) if not m.startswith('_')]

['add_done_callback', 'cancel', 'cancelled', 'done', 'exception', 'get_loop', 'remove_done_callback', 'result', 'set_exception', 'set_result']

>>> future.result() #这个时候如果直接调用result()方法会报错

Traceback (most recent call last):

File "", line 1, in

asyncio.base_futures.InvalidStateError: Result is not set.

>>> async def runfun():

... result=await future

... print(result)

...

>>>loop.run_until_complete(runfun()) #也可以通过 loop.run_until_complete(future) 来执行,这里只是为了演示await

111

>>> future

>>> future.done()

True

>>> future.result()

111

Task

Eventloop除了支持协程,还支持注册Future和Task2种类型的对象,而Future是协程的封装,Future对象提供了很多任务方法(如完成后的回调,取消,设置任务结果等等),但是一般情况下开发者不需要操作Future这种底层对象,而是直接用Future的子类Task协同的调度协程来实现并发。那么什么是Task呢?下面介绍下:

一个与Future类似的对象,可运行Python协程。非线程安全。

Task对象被用来在事件循环中运行协程。如果一个协程在等待一个Future对象,Task对象会挂起该协程的执行并等待该Future对象完成。当该Future对象完成被打包的协程将恢复执行。

事件循环使用协同日程调度: 一个事件循环每次运行一个Task对象。而一个Task对象会等待一个Future对象完成,该事件循环会运行其他Task、回调或执行IO操作。

下面看看用法:

>>> async def a():

... print('Suspending a')

... await asyncio.sleep(3)

... print('Resuming a')

...

>>> task = asyncio.ensure_future(a())

>>> loop.run_until_complete(task)

Suspending a

Resuming a

asyncio中一些常见用法的区别

Asyncio.gather和asyncio.wait

我们在上面的代码中用到过asyncio.gather,其实还有另外一种用法是asyncio.wait,他们都可以让多个协程并发执行,那么他们有什么区别呢?下面介绍下。

>>> import asyncio

>>> async def a():

... print('Suspending a')

... await asyncio.sleep(3)

... print('Resuming a')

... return 'A'

...

...

... async def b():

... print('Suspending b')

... await asyncio.sleep(1)

... print('Resuming b')

... return 'B'

...

>>> async def fun1():

... return_value_a, return_value_b = await asyncio.gather(a(), b())

... print(return_value_a,return_value_b)

...

>>> asyncio.run(fun1())

Suspending a

Suspending b

Resuming b

Resuming a

A B

>>> async def fun2():

... done,pending=await asyncio.wait([a(),b()])

... print(done)

... print(pending)

... task=list(done)[0]

... print(task)

... print(task.result())

...

>>> asyncio.run(fun2())

Suspending b

Suspending a

Resuming b

Resuming a

{:1> result='A'>, :8> result='B'>}

set()

:1> result='A'>

A

根据上述代码,我们可以看出两者的区别:

asyncio.gather能收集协程的结果,而且会按照输入协程的顺序保存对应协程的执行结果,而asyncio.wait的返回值有两项,第一项是完成的任务列表,第二项表示等待完成的任务列表。

asyncio.wait支持接受一个参数return_when,在默认情况下,asyncio.wait会等待全部任务完成(return_when='ALL_COMPLETED'),它还支持FIRST_COMPLETED(第一个协程完成就返回)和FIRST_EXCEPTION(出现第一个异常就返回):

>>> async def fun2():

... done,pending=await asyncio.wait([a(),b()],return_when=asyncio.tasks.FIRST_COMPLETED)

... print(done)

... print(pending)

... task=list(done)[0]

... print(task)

... print(task.result())

...

>>> asyncio.run(fun2())

Suspending a

Suspending b

Resuming b

{:8> result='B'>}

{:3> wait_for=()]>>}

:8> result='B'>

B

一般情况下,用asyncio.gather就足够了。

asyncio.create_task和loop.create_task以及asyncio.ensure_future

这三种方法都可以创建Task,从Python3.7开始可以统一的使用更高阶的asyncio.create_task.其实asyncio.create_task就是用的loop.create_task.

loop.create_task接受的参数需要是一个协程,但是asyncio.ensure_future除了接受协程,还可以是Future对象或者awaitable对象:

如果参数是协程,其底层使用loop.create_task,返回Task对象

如果是Future对象会直接返回

如果是一个awaitable对象,会await这个对象的__await__方法,再执行一次ensure_future,最后返回Task或者Future。

所以ensure_future方法主要就是确保这是一个Future对象,一般情况下直接用asyncio.create_task就可以了。

注册回调和执行同步代码

可以使用add_done_callback来添加成功回调:

def callback(future):

print(f'Result:{future.result()}')

def callback2(future, n):

print(f'Result:{future.result()}, N:{n}')

async def funa():

await asyncio.sleep(1)

return "funa"

async def main():

task = asyncio.create_task(funa())

task.add_done_callback(callback)

await task

#这样可以为callback传递参数

task = asyncio.create_task(funa())

task.add_done_callback(functools.partial(callback2, n=1))

await task

if __name__ == '__main__':

asyncio.run(main())

执行同步代码

如果有同步逻辑,想要用asyncio来实现并发,那么需要怎么做呢?下面看看:

def a1():

time.sleep(1)

return "A"

async def b1():

await asyncio.sleep(1)

return "B"

async def main():

loop = asyncio.get_running_loop()

await asyncio.gather(loop.run_in_executor(None, a1), b1())

if __name__ == '__main__':

start = time.perf_counter()

asyncio.run(main())

print(f'main method Cost:{time.perf_counter() - start}')

# 输出: main method Cost: 1.0050589740000002

可以使用run_into_executor来将同步函数逻辑转化成一个协程,第一个参数是要传递concurrent.futures.Executor实例的,传递None会选择默认的executor。

python asyncio理解_深入理解Python中的asyncio相关推荐

  1. python asyncio理解_深入理解asyncio(二)

    Asyncio.gather vs asyncio.wait 在上篇文章已经看到多次用asyncio.gather了,还有另外一个用法是asyncio.wait,他们都可以让多个协程并发执行.那为什么 ...

  2. python agg函数_个人对Pandas中agg、apply和transform函数的理解

    个人对Pandas中agg.apply和transform函数的理解 学习<利用Python进行数据分析>一书,关于pandas的这三个函数,个人理解如下. agg agg方法可以被gro ...

  3. win10系统64位安装python什么版本_如何在win10中安装Python

    本文主要讲解win10如何安装python,希望对初学的小伙伴有帮助.环境:win 10 64位操作系统1.python下载https://www.python.org/downloads/2.x和3 ...

  4. python标题行_如何在python datafram中更改标题行

    在python中使用pandas更改现有数据帧中的头行时遇到问题.导入pandas和csv文件后,我将一个标题行设置为None,以便能够在转置后删除重复的日期.但是这会留下一个我不想要的行标题(实际上 ...

  5. python优化网站_[练习] 用PYTHON来优化网站中的图片

    我到公司以来,第一次加班,哇,加一晚上加一上午,现在还没下班的迹象,555,困. 对于网站中的一些关键的页面,多重缓存.静态化.程序代码优化--之外,为了提高用户打开页面的速度,图片是必须要优化的. ...

  6. load python txt文件_详解Python中numpy.loadtxt()读取txt文件

    为了方便使用和记忆,有时候我们会把 numpy.loadtxt() 缩写成np.loadtxt() ,本篇文章主要讲解用它来读取txt文件. 读取txt文件我们通常使用 numpy 中的 loadtx ...

  7. java的接口怎么理解_如何理解Java 中的接口

    一.接口(Interface) 在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明.一个类通过继承接口的方式,从而来继承接口的抽象方法.接口并不是类,编写接口的方 ...

  8. python turtle 绘图_谈一下Pycharm中关联系统Python解释器的方法

    大家知道,PyCharm是一款著名的Python IDE开发工具,是拥有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,具备基本的调试.语法高亮.Project管理.代码跳转.智能提示 ...

  9. python 获取当前目录_如何在Python中获取当前的工作目录?

    python 获取当前目录 To get the current working directory in Python, there is a library function getcwd() i ...

最新文章

  1. LNMP环境搭建(二)集成Nginx与PHP
  2. 重复数据删除(De-duplication)技术研究
  3. TreeView获取目录下的所有文件
  4. LightOJ - 1179 Josephus Problem(约瑟夫环)
  5. PowerShell yarn : 无法加载文件 C:\Users\Admin\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本。
  6. J2EE如何生成验证码图片和点击刷新验证码
  7. 基于JAVA+Servlet+JSP+MYSQL的图书馆座位预约系统
  8. Shell: 文本文件操作
  9. RemapKey等:小巧实用的键盘映射工具
  10. java网页制作会说话的汤姆猫_android 仿 会说话的汤姆猫 源码
  11. 服务器经常被攻击怎么办?这7个重要因素要做好!
  12. 商品期货基本面研究纲要
  13. hanlp自然语言处理包的基本使用--python
  14. 软考高级-系统架构师-案例分析-数据库真题考点汇总
  15. matlab对AMD显卡的支持,matlab - 深入学习Matlab与AMD显卡 - 堆栈内存溢出
  16. iPhone13首批售罄连夜补货,粉色款爆红,天猫3分钟售罄!
  17. 犹抱琵琶半遮面--探究直播系统源码的真面目
  18. 三十七. geotrellis使用 COG 基础介绍
  19. springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
  20. SpringBoot Configuration Annotation Processor not configured

热门文章

  1. 常用的android自动化测试技术
  2. 使用python flask框架开发图片上传接口 详细案例
  3. mysql内连接之等值连接
  4. 2022年 HSC-1th中CRYPTO的Easy SignIn
  5. 华为OJ——整数与IP地址间的转换
  6. 绩效考核六大认识误区解析(zt)
  7. Mysql——FROM_UNIXTIME和UNIX_TIMESTAMP函数的用法
  8. 恢复删除陌陌聊天记录方法
  9. 我是如何实现鸿蒙系统编译、烧录不跨平台的?
  10. browserify的介绍