python中的协程,异步编程async,await

一、什么是协程

 理解协程:https://www.cnblogs.com/liyuanhong/articles/14268571.html

二、实现协程的几种方法

  1. greenlet ,早期模块
  2. yield关键字
  3. asyncio装饰器(python3.4)
  4. async、await 关键字(python3.5)

2.1 greenlet 实现协程

from greenlet import greenlet
def test1():
    print(1)      # 第二步,输出1
    gr2.switch()  # 第三部,切换到test2
    print(2)      # 第六部,输出2
    gr2.switch()  # 第七部,切换到test2
def test2():
    print(3)      # 第四部,输出3
    gr1.switch()  # 第五部,切换到test1
    print(4)      # 第八步,输出4
gr1 = greenlet(test1)
gr2 = greenlet(test2)

gr1.switch()      # 第一步,去执行test1函数

执行结果:

 2.2 yield去实现协程

def test1():
    yield 1      
    yield from test2()  
    yield 2     
def test2():
    yield 3     
    yield 4    

f = test1()
for i in f:
    print(i)

执行结果:

 2.3 asyncio 实现协程(python3.4 以及之后的版本可用)

注意:对基于生成器的协程的支持已被弃用,并在Python 3.11中被删除,所以一下代码会在@asyncio.coroutine 报错,因此,您需要在def之前使用**async关键字**,详见2.4的代码

import asyncio
@asyncio.coroutine
def test1():
    print(1)
    yield from asyncio.sleep(2)  # 遇到熊等待,自动化切换到task其他任务
    print(2)

@asyncio.coroutine
def test2():
    print(3)
    yield from asyncio.sleep(2)  # 遇到熊等待,自动化切换到task其他任务
    print(4)
task = [
asyncio.ensure_future(test1()),
asyncio.ensure_future(test2())
]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(task))

2.4 async 与 await 关键字

import asyncio

async def test1():
    print(1)
    await asyncio.sleep(2)  # 遇到熊等待,自动化切换到task其他任务
    print(2)


async def test2():
    print(3)
    await asyncio.sleep(2)  # 遇到熊等待,自动化切换到task其他任务
    print(4)

task = [
    asyncio.ensure_future(test1()),
    asyncio.ensure_future(test2())
]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(task))

执行结果

 三、asyncio 事件循环

3.1事件循环

什么是事件循环:可以理解为一个死循环,去检测并执行某些代码。如下伪代码:

任务列表 = [任务1,任务2,任务3,...]

while Ture:
    可执行的任务列表,已完成任务列表 = 去任务列表中查询所有的任务,将可执行任务和已完成任务返回
    for 就绪任务 in 可执行的任务列表:
        执行已就绪任务

    for 已完成任务 in 已完成任务列表:
        在任务列表中一次已完成的任务

    如果任务列表中的任务都已完成,则终止循环
import asyncio

# 去获取一个事件循环
loop = asyncio.get_event_loop()
# 将任务放到任务列表
loop.run_until_complete(asyncio.wait(task))

四、协程函数与协程对象

 协程函数,定义函数使用: async  def  函数名。

协程对象,执行协程函数,得到的对象就是协程对象。(执行协程函数,内部代码不会执行,只会得到一个协程对象)

async def test():
    print("hello")

ob = test()  # 执行协程函数,得到一个协程对象,函数内部代码不会执行
print(ob)

执行结果

 如果要执行协程函数,需要用事件循环去执行协程函数

import asyncio
async def test():
    print("hello")

ob = test()
# 使用事件循环去执行协程函数
loop = asyncio.get_event_loop()
loop.run_until_complete(ob)

执行结果:

 在python3.7以后会有更加简便的写法:

import asyncio
async def test():
    print("hello")

ob = test()
# python3.7以后可以使用这种简便写法
asyncio.run(ob)

 五、await 关键字

await后面一般跟一个可等待的对象(协程对象、future对象、Task对象 -> IO等待)

import asyncio
async def eat():
    print("走吃饭")
    await asyncio.sleep(2)  # 遇到IO等待会自动切换其他任务
    print("饭吃完了")
    return "完成"

async def main():
    print("main start")
    # 创建task对象
    task1 = asyncio.create_task(eat())
    task2 = asyncio.create_task(eat())
    print("main end")

    # 遇到IO等待会自动切换其他任务
    res1 = await task1
    res2 = await task2
    print(res1,res2)

asyncio.run(main())

执行结果:

 上面的代码一般会按照下面的方式去写:

import asyncio
async def eat():
    print("走吃饭")
    await asyncio.sleep(2)  # 遇到IO等待会自动切换其他任务
    print("饭吃完了")
    return "完成"

async def main():
    print("main start")
    # 创建task对象
    task_list = [
        asyncio.create_task(eat()),
        asyncio.create_task(eat())
    ]
    print("main end")
    # asyncio.wait返回两个值,默认任务列表的返回值都会放到done里
    done,pendding = await asyncio.wait(task_list)
    print(done)
    print(done.pop().result())

asyncio.run(main())

执行结果如下:

 六、Task对象

Task对象用于并发调度协程,通过asyncio.create_task(协程对象) 的方式创建Task对象(在python3.7中加入的该函数)。这样可以让协程加入事件循环中等待调度执行。除了使用asyncio.create_task函数以外,还可以使用低层级的loop.create_task()或 ensure_future()函数,不建议手动实例化Task对象。

七、异步上下文管理器

对象通过定义 __aenter__ ()和 __aexit__() 方法来对 async with 中的环境进行控制。

async with 不能单独存在,必须嵌套到一个协程函数中才能使用,否则会报错,例如:

import asyncio

class AsynConnectDb():
    def __init__(self):
        self.conn = None

    async def do_something(self):
        print("操作数据库")
        return "返回结果"

    async def __aenter__(self):
        print("连接数据库")
        await asyncio.sleep(1)
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        print("数据库连接断开")
        await asyncio.sleep(1)
# async with 不能单独存在,必须嵌套到一个协程函数中才能使用 async def func(): async
with AsynConnectDb() as db: result = await db.do_something() print(result) asyncio.run(func())

运行结果:

 八、异步迭代器

什么是异步迭代器

实现了 __aiter__() 和  __anext__() 方法的对象。__anext__()必须返回一个 awaitable 对象。async for 会处理异步迭代的 __anext__()方法返回的可等待对象。知道引发一个StopAsyncIteration异常。

什么事异步迭代对象

可在 async for 语句中使用的对象。必须通过它的__aiter__()方法返回一个 asynchronous iterator。

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 == 5:
            return None
        return self.count

    def __aiter__(self):
        return self

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

# async for不能单独存在,必须嵌套到一个协程函数中才能使用 async def func(): async
for item in Reader(): print(item) asyncio.run(func())

运行结果:

 

posted @ 2024-05-29 21:15  远洪  阅读(166)  评论(0)    收藏  举报