A-A+

python 百万「并发」基础之异步编程详解

2021年08月20日 15:47 汪洋大海 暂无评论 共3927字 (阅读581 views次)

asyncio 以及 async/await 简介

在 Python3.4 中引入了异步 I/O 框架 asyncio,该框架在 Python3.5 中被完善并作为标准库之一,用于基于协程的异步 I/O 编程,本节就来讨论一下 asyncio 以及 async/await 等内容。

asyncio 以及 async/await 关键概念

在 Python3.4 中引入了 asyncio.coroutine 装饰器来标志函数作为协程函数,协程函数具有协程的特性并与 asyncio 的事件循环一同使用,实现异步编程的目的,为了避免生成器与协程之间的混淆,在 Python3.5 中引入了 async/await,其中 async 替代 asyncio.coroutine 装饰器,await 替代 yield from,从而让协程的实现更加直观,async/await 和 yield frome 这两种不同风格的协程在底层其实是相互复用相互兼容的,在 Python3.6 中 asyncio 库 “转正”,成为正式的标注库。

这里只讨论新的写法,即 async/await 实现协程的方式,Python 中协程主要的特性如下:

????1. 函数使用了 async 表达式开头,即使它不包含 await 表达式,也是一个协程函数 ????2.async 协程函数中使用 yield 或者 yield from 会报 SyntaxError 错误,即新旧语法不可混合使用 ????3. 与常规生成器类似,协程函数在调用时会返回一个 coroutine 对象 ????4. 与 yield 方式实现的协程不同,yield 在最后会抛出 Stoplteration 异常,而 async 中则是 RuntimeError ????5. 当 async 创建的协程函数被垃圾回收时,一个未被 await 的协程会抛出 RuntimeWarning 异常。

在使用 asyncio 框架前,需要先了解其中几个概念。

Coroutine 协程
以 async 表达式开头的函数成为协程函数或简称为协程,如下:

1
2
3
4
5
6
import asyncio
async def main():
    print('hello')
    await asyncio.sleep(1)
    print('world')
asyncio.run(main())

上述代码中,main () 为协程函数,代码写法是 Python3.7 的写法,Python3.7 中对 asyncio 的使用做了简化,????如果你使用 python3.6 为主,其 asyncio 写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import asyncio
async def main():
    print('hello')
    await asyncio.sleep(1)
    print('world')
def run():
    # 创建协程对象
    coroutine = main()
    # 创建事件循环
    loop = asyncio.get_event_loop()
    # 将协程对象添加到事件循环中,运行直到结束
    loop.run_until_complete(coroutine)
    # 关闭事件循环
    loop.close()
run()

可以看出,Python3.6 中 asyncio 的用法会复杂一些,在 Python3.7 中,run () 方法已经为我们处理好了创建事件、添加协程对象到事件循环、关闭事件循环等事情????,即

1
2
3
4
5
loop asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
# 在Python3.7中被替换为
asyncio.run()

# 在Python3.7中被替换为
asyncio.run()
当然,asyncio 也支持传统的基于生成器的协程,不再多提。

Awaitables 可等待对象
「可等待对象」通常有 3 类,分别是:

????1. 协程 coroutine ????2. 任务 Task ????3. 未来对象 Future

一个直观的判断方法就是,如果一个对象能够被用在 await 表达式中,那么就可以称这个对象为 「可等待对象」

简单示例如下:

1
2
3
4
5
6
7
8
9
import asyncio
async def myprint():
    print('hello hackpython')
async def main():
    # 直接调用,创建协程对象,不会执行协程中的内容
    myprint()
    # 协程对象成为被等待对象后,才会执行其中的内容
    await myprint()
asyncio.run(main())

上述代码会输出如下内容:

use_asyncio4.py:8: RuntimeWarning: coroutine 'myprint' was never awaited
myprint()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
hello hackpython

从输入内容可以看出,直接使用协程函数是不会执行其中的逻辑的,而且还会因为没有使用该协程对象而触发相应的警告,????只有利用 await 成为可等待对象后,才会被 asyncio 事件循环去执行。await 会将控制权交由可等待对象。

asyncio 以及 async/await  Task 任务

「任务」主要用于「并发」的调度协程。

一个协程可以通过 asyncio.create_task () 函数封装成一个 Task,此时这个协程很快就会被自动调度执行,代码如下:

1
2
3
4
5
6
7
8
9
import asyncio
async def fun1():
    print('hackpython')
async def main():
    # 创建任务
    task = asyncio.create_task(fun1())
    # 作为被等待对象
    await task
asyncio.run(main())

asyncio.createtask () 是 Python 3.7 新增的方法,如果是 Python3.6 还可以用 asyncio.ensure_future 和 loop.create_task????。

可以将 Task 理解为协程对象的进一步封装,其中包含着各种状态,简单使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async def a():
    print('a funtion start.')
    await asyncio.sleep(2)
    print('a function end.')
async def main():
    # task = asyncio.ensure_future(a())
    task = asyncio.create_task(a())
    print(task)
    print(task.done())
    await task
    print(task)
    print(task.done())
if __name__ == '__main__':
    asyncio.run(main())

上述代码输出如下:

<Task pending coro=<a() running at /Users/ayuliao/Desktop/spider/lianjia/use_asyncio1.py:3>>
False
a funtion start.
a function end.
<Task finished coro=<a() done, defined at /Users/ayuliao/Desktop/spider/lianjia/use_asyncio1.py:3> result=None>
True

从输出内容可以看出,一开始 Task 任务的状态为 pending (等待状态),调用其 done () 方法可判断该任务是否执行????,可以看出,没有利用 await 将其转为可等待对象前,Task 任务是没有执行的,使用 await 后,即将控制权交由 task 对象,此时再次打印,发现其状态改变为 finshed (完成状态),调用 done () 方法后可得该任务被正常执行了????。

asyncio 以及 async/await Future 未来对象

Future 代表着一个未来对象,当异步操作结束后会将最终的结果设置到 Future 对象上,Future 同样是对协程的封装,它是一个偏底层的类,具有比较多的方法可以做一些复杂的操作,但在日常开发时,并不会去使用????,更多的是使用 Task 任务,它其实是 Future 的一个子类????。

asyncio 以及 async/await Eventloop 事件循环

使用 asyncio 框架时,其实就是开启一个事件循环,事件循环对应的实例提供了注册、取消、执行与回调等方法,方便控制整个事件循环实例。

所谓事件循环,就是将协程函数、任务 Task、未来对象 Future 等注册到事件循环中,事件循环实例会循环执行这些函数????,注意同一时刻下只执行某个函数对象,具体执行某个函数时,如果执行到函数中进行 I/O 耗时操作的部分,事件循环就会将该函数暂停,而去执行其他函数,等进行 I/O 耗时操作的函数执行完后,会再次加入循环队列,等事件循环下次循环到它时继续从此前位置执行,从而实现这些可异步操作对象的协同运行,达到并发的效果????。

asyncio 以及 async/await 结尾

asyncio 是 Python 中比较复杂但又非常重要的概念,在 <百万「并发」基础之异步编程> 上、中、下三篇文章中比较系统的讨论了异步编程的概念以及在 Python 中的实现方式????,但对生成器、yield from 以及 asyncio 等依旧没有深入探讨????,在 HackPython 后面的文章中,会系统性讨论这些概念,最后欢迎学习 HackPython 的教学课程并感觉您的阅读与支持。

 

文章来源:https://blog.csdn.net/weixin_30230009/article/details/105100103

文章参考:最新python request使用异步协程async/await详解   https://woj.app/7477.html

布施恩德可便相知重

微信扫一扫打赏

支付宝扫一扫打赏

×
标签:

给我留言