基于协程的异步编程知识点总结


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

什么是协程?

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

常用的实现协程的方式

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

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

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

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

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

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 对象的时候, 给它存放多个协程对象.

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 的协程对象来调用迭代器.

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())

异步上下文管理器

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

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())

文章作者: 陌上人如玉
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 陌上人如玉 !
 上一篇
Redis的搭建和使用 Redis的搭建和使用
前段时间,学习 scrapy 框架入门的时候, 连接Redis 没有连接成功, 但是还不知道怎么弄, 留下了疑问.昨晚学习异步编程的时候, 异步操作Redis 的时候, 发现了问题, 总算是解决了之前遗留下来的连接问题.
下一篇 
scrapy 框架遇到的问题 scrapy 框架遇到的问题
近来学习scrapy 框架, 遇到了不少框架内的坑, 有的解决了,有的还未解决,先记录一下.
2021-01-04
  目录