asynico异步编程高层级API

一、前言

  之前写过 asynico 异步编程的文章,写那篇博客的时候 python 最新官方版本是3.6+。几个月后发布了 python3.7,这次版本更新对 asynico 改动挺大的,官方推出了一套 高层级的API,其实就是封装了原来那套低层级的API。

  python 通过协程来实现异步编程,因此我们首先来了解下协程。

二、协程

1)协程通过 async/await 语法进行声明,使用 asyncio.run() 函数执行协程,此函数会运行传入的协程,负责管理 asyncio 事件循环,终结异步生成器,并关闭线程池。如下代码所示:

import asyncio,datetime

async def main():
    print(datetime.datetime.now())
    await asyncio.sleep(1)
    print(datetime.datetime.now())

if __name__ =='__main__':
    asyncio.run(main())

 

2)如果一个对象想在 await 语句中使用,那么它必须是 可等待 对象。许多 asyncio API 都被设计为接受可等待对象,可等待 对象有三种主要类型:协程、任务 和 Future

  • 协程

  Python中的协程也是可等待对象,因此可被 await。如 price 协程方法:

import asyncio


async def price(x,y):
    return x*y

async def main():
    P = await price(1,2)
    print(P)

if __name__ =='__main__':
    asyncio.run(main())

 

  • 任务

  当协程通过 asyncio.create_task()方法 被封装成一个任务,并自行调度执行任务,该方法 python 3.7版本被加入,低版本使用 asyncio.ensure_future() 。

import asyncio

async
def price(x,y): return x*y async def main(): task = asyncio.create_task(price(1,2)) P = await task print(P) if __name__ =='__main__': asyncio.run(main())

 

  • Future

  Future 是一种特殊的 低层级 可等待对象,表示一个异步操作的最终结果。官网 不建议在应用层级的代码中创建 Future 对象,因此没有深入了解。

 

三、并发任务

  线程在遇到IO等待时,会被阻塞,并 交出CPU控制权,换句话来说由 系统控制线程切换。协程遇到IO等待时,则由 程序控制,不会被阻塞,也 不会交出CPU控制权,而是直接执行下一个事务。所以协程在处理IO密集型任务时,资源开销小,效率极高,优于多线程。

  使用 asyncio.gather(*aws) 并发运行协程,如果所有可等待对象都成功完成,结果将是一个由所有返回值聚合而成的列表。结果值的顺序与 aws 中可等待对象的顺序一致。

import asyncio,datetime

async def price(x,y):
    await asyncio.sleep(1)
    print(datetime.datetime.now())
    return x*y

async def main():
    P = await asyncio.gather(price(1,2),
                             price(2,4))
    print(P)

if __name__ =='__main__':
    asyncio.run(main())


#输出
#2021-11-17 20:11:17.180417
#2021-11-17 20:11:17.180417
#[2, 8]

 

低层级 API 和 高层级 API 代码对比 

  • 低层级 API 代码如下是 python 3.7 以下版本 实现并发的代码:
import time
import asyncio
from aiohttp import ClientSession

tasks = []
url = "https://www.baidu.com/{}"
async def hello(url):
    async with ClientSession() as session:
        async with session.get(url) as response:
#            print(response)
            print('Hello World:%s' % time.time())
            return await response.read()

def main():
    for i in range(5):
        task = asyncio.ensure_future(hello(url.format(i)))
        tasks.append(task)
    result = loop.run_until_complete(asyncio.gather(*tasks))
    print(result)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    main()


# 输出
'''
Hello World:1637152588.856801
Hello World:1637152588.856801
Hello World:1637152588.8577979
Hello World:1637152588.8619971
Hello World:1637152588.864993
[b'<!DOCTYPE HTML PUBLIC "- ...
'''

 

  • 高层级 API 代码,省去了显式的定义事件循环等,看上去更加简洁、容易理解:
import time
import asyncio
from aiohttp import ClientSession

tasks = []
url = "https://www.baidu.com/{}"
async def hello(url):
    async with ClientSession() as session:
        async with session.get(url) as response:
            print('Hello World:%s' % time.time())
            return await response.read()

async def main():
    hello_list = [hello(url.format(i)) for i in range(5)]
    P = await asyncio.gather(*hello_list)
    print(P)

if __name__ == '__main__':
    asyncio.run(main())


# 输出
'''
Hello World:1637152648.7986703
Hello World:1637152648.8146682
Hello World:1637152648.8151577
Hello World:1637152648.8450766
Hello World:1637152648.8491611
[b'<!DOCTYPE HTML PUBLIC "-/...
'''

 

 asynico 还处于不断完善阶段,版本升级后都会有些小的变动,请及时查阅文档:asynico官方文档

 

 

posted @ 2022-01-06 14:53  三只松鼠  阅读(518)  评论(0编辑  收藏  举报