近来学习了基于协程的异步编程, 做个知识点总结一下.

什么是协程?

协程不是计算机提供, 是程序员人为创造的. 协程也可以称为微线程, 是一种用户态内的上下文切换技术, 简而言之, 其实就是通过一个线程实现代码块相互切换执行.

常用的实现协程的方式

官方推荐我们使用 async 和 await 关键字 , 但是必须是 python3.5之后的版本.

协程的意义以及同步,异步

在一个线程中, 如果遇到IO 等待, 线程不会傻等, 利用空闲的时间去做其他事情.
普通方法: 先把三个 url 装在一个 list 里, 用 for 循环, 一个 url , 一个url 去下载.
使用协程: 在下载等待第一个 url 返回数据的时候,就 发送第二个请求去下载第二部电影, 依次类推, 可以提升效率.
因此, 我们上面的普通方法也叫同步方式, 使用协程的方式, 也就是异步方式(基于协程的异步方式, 实现异步的方式还有线程池, 进程池)

基于协程的异步编程的基本实现

事件循环: 可以理解成一个死循环, 检测到有需要执行的代码就会去执行.
协程函数: 定义一个函数的时候, 在函数名前面加上一个 async , 即 async def func() 这样的函数, 就是协程函数.
协程对象: 执行协程函数, 就会返回一个协程对象.
注意: 协程函数() 的时候, 并不会执行函数内部的代码, 只会生成一个协程对象, 如果要执行协程函数里的内容, 就需要将协程对象交给事件循环来处理.

1
2
3
4
5
async def func():
await asynio.sleep(1)

loop = asynico.get_event_loop()
loop.run_until_complete(func())

在 python3.7之后, 就可以直接 asyncio.run(协程对象/task对象) ,就不需要还实例化 loop, 然后用 loop 来执行协程对象/task 对象了.

await 的使用

await + 可等待对象(协程对象, Future, Task对象-> 都属于IO 等待对象 )
注意: await 就是等待对象的值得到结果之后再继续向下走, 如果有多个 await 就要注意了. 一般一个 await 是等待一个 task的.

Task对象

简单白话:就是在事件循环中添加多个任务的. 所以我们可以在创建一个 task 对象的时候, 给它存放多个协程对象.

1
2
3
4
5
6
7
8
9
10
11
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return "返回值"

task = [func(), func()]
loop1 = asyncio.get_event_loop()
done,pending = loop1.run_until_complete(asyncio.wait(task))
print(done)

asyncio异步迭代器

其实就是和一般意义上的迭代器差不多, 只是用于异步中而已.其实异步迭代器, 也就是在 iter和next 方法前面都加了一个 a , aiter和 anext 方法, 同时拥有这两个方法的就是异步迭代器. aiter返回self, anext 返回的是一个 awaitable 对象.
其中有个注意事项, async for 必须写在协程函数内部, 否则语法就会报错. 我们通过这个有 async for 的协程对象来调用迭代器.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import asyncio

class Reader:
"""
自定义异步迭代器(同时也是异步可迭代对象)
"""
def __init__(self):
self.count = 0

async def readline(self):
# await asyncio.sleep(1)
self.count +=1
if self.count == 100 :
return None
return self.count

# iter方法是返回 self 自身
def __aiter__(self):
return self

async def __anext__(self):
val = await self.readline()
if val == None:
raise StopAsyncIteration
return val

async def func():
obj = Reader()
# async for 只能写在协程函数里, 否则就会语法报错
async for item in obj:
print(item)

loop = asyncio.get_event_loop()
loop.run_until_complete(func())

异步上下文管理器

和一般的上下文管理器类似, 都是先 执行什么, 最后再执行什么.只是异步上下文管理器用于异步编程.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import asyncio

class DoingSomething:
def __init__(self, flag):
self.flag = flag

async def do_something(self):
return "我们的目标是" + self.flag

async def __aenter__(self):
print("立项了")
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
print("项目结束")
await asyncio.sleep(1)

async def func():
async with DoingSomething("法国冷冻柜") as f:
result = await f.do_something()
print(result)

loop = asyncio.get_event_loop()
loop.run_until_complete(func())