【python】详解异步async库的使用

在学习asyncio之前,先理清楚同步/异步的概念:
  • 同步是指完成事务的逻辑,先执行第一个事务,如果阻塞了,会一直等待,直到这个事务完成,再执行第二个事务,顺序执行
  • 异步是和同步相对的,异步是指在处理调用这个事务的之后,不会等待这个事务的处理结果,直接处理第二个事务去了,通过状态、通知、回调来通知调用者处理结果
asyncio函数:

异步IO采用消息循环的模式,重复“读取消息—处理消息”的过程,也就是说异步IO模型”需要一个消息循环,在消息循环中,主线程不断地重复“读取消息-处理消息”这一过程

  • event_loop 事件循环:程序开启一个无限的循环,程序员会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。
  • coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
  • task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。
  • async/await 关键字: 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。
一、asyncio

下面通过举例来对比同步代码和异步代码编写方面的差异,其次看下两者性能上的差距,使用asyncio.sleep(1)模拟耗时1秒的io操作。

  • 同步代码:
import time

def hello():
    time.sleep(1)

def run():
    for i in range(5):
        hello()
        print('Hello World:%s' % time.time())  
if __name__ == '__main__':
    run()

Hello World:1536842494.2786784
Hello World:1536842495.2796268
Hello World:1536842496.2802596
Hello World:1536842497.2804587
Hello World:1536842498.2812462
  • 异步代码:
import time
import asyncio

# 定义异步函数
async def hello():
    print('Hello World:%s' % time.time())
    #必须使用await,不能使用yield from;如果是使用yield from ,需要采用@asyncio.coroutine相对应
    await asyncio.sleep(1)   
    print('Hello wow World:%s' % time.time())

def run():
    tasks = []
    for i in range(5):
        tasks.append(hello())
    loop.run_until_complete(asyncio.wait(tasks))

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

Hello World:1536855050.1950748
Hello World:1536855050.1950748
Hello World:1536855050.1950748
Hello World:1536855050.1960726
Hello World:1536855050.1960726
(暂停约1秒)
Hello wow World:1536855051.1993241
Hello wow World:1536855051.1993241
Hello wow World:1536855051.1993241
Hello wow World:1536855051.1993241
Hello wow World:1536855051.1993241

async def 用来定义异步函数,其内部有异步操作。每个线程有一个事件循环,主线程调用asyncio.get_event_loop()时会创建事件循环,把异步的任务丢给这个循环的run_until_complete()方法,事件循环会安排协同程序的执行。上述程序中,hello()会首先打印出Hello world!,然后,yield from语法可以让我们方便地调用另一个generator。由于await asyncio.sleep(1)也是一个coroutine,所以线程不会等待asyncio.sleep(1),而是直接中断并执行下一个消息循环。当asyncio.sleep(1)返回时,线程就可以从yield from拿到返回值(此处是None),然后接着执行下一行语句。

把asyncio.sleep(1)看成是一个耗时1秒的IO操作,在此期间,主线程并未等待,而是去执行EventLoop中其他可以执行的coroutine了,因此可以实现并发执行。

asyncio操作的总结:
  • async def hello(): 定义async异步函数,中间可以添加await async.sleep(N) 来设定中断并执行下一个循环消息
  • tasks = [] 任务则是对协程进一步封装,其中包含任务的各种状态。即多个coroutine函数可以封装成一组Task然后并发执行
  • loop = asyncio.get_event_loop() #获取“事件循环”对象
  • loop.run_until_complete(asyncio.wait(tasks)) #通过事件循环,去调用协程函数
  • loop.close() 结束时间循环
二、aiohttp

  如果需要并发http请求,通常是用requests,但requests是同步的库,如果想异步的话需要引入aiohttp。这里引入一个类,from aiohttp import ClientSession,首先要建立一个session对象,然后用session对象去打开网页。session可以进行多项操作,比如post, get, put, head等。

  • 基本用法:
async with ClientSession() as session:
    async with session.get(url) as response:
  • aiohttp异步实现的例子:
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:
            response = await response.read()
            print(response)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(hello(url))

首先async def 关键字定义了这是个异步函数,await 关键字加在需要等待的操作前面,response.read()等待request响应,是个耗IO操作。然后使用ClientSession类发起http请求。

  • 多链接异步访问

如果我们需要请求多个URL该怎么办呢,同步的做法访问多个URL只需要加个for循环就可以了。但异步的实现方式并没那么容易,在之前的基础上需要将hello()包装在asyncio的Future对象中,然后将Future对象列表作为任务传递给事件循环。

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:
            response = await response.read()
            print('Hello World:%s' % time.time())

def run():
    for i in range(5):
        task = asyncio.ensure_future(hello(url.format(i)))
        tasks.append(task)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    run()
    loop.run_until_complete(asyncio.wait(tasks))

Hello World:1536843566.064149
Hello World:1536843566.070586
Hello World:1536843566.0769563
Hello World:1536843566.0779328
Hello World:1536843566.0799286
  • ·收集http响应

好了,上面介绍了访问不同链接的异步实现方式,但是我们只是发出了请求,如果要把响应一一收集到一个列表中,最后保存到本地或者打印出来要怎么实现呢,可通过asyncio.gather(*tasks)将响应全部收集起来

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

Hello World:1536843488.678779
Hello World:1536843488.6797836
Hello World:1536843488.6867576
Hello World:1536843488.6877556
Hello World:1536843488.6877556
异步编程是一种编程模式,它允许程序在等待某些操作完成时继续执行其他任务,而不是阻塞整个程序的执行。Python中的异步编程可以使用`async`和`await`关键字来实现。 `async`关键字用于定义一个异步函数,这个函数可以使用`await`关键字来挂起自身的执行,等待其他的异步操作完成后再继续执行。 下面是一个简单的例子,展示了如何使用`async`和`await`来实现异步编程: ```python import asyncio async def async_task(): print("异步任务开始") await asyncio.sleep(1) # 模拟耗时操作 print("异步任务结束") async def main(): print("主程序开始") await asyncio.gather(async_task(), async_task()) # 并发执行两个异步任务 print("主程序结束") asyncio.run(main()) ``` 在上面的例子中,我们定义了一个异步函数`async_task()`,它会打印一些信息,然后使用`await asyncio.sleep(1)`来模拟一个耗时操作。然后我们定义了另一个异步函数`main()`,它会并发地执行两个`async_task()`函数。最后,在`asyncio.run(main())`中运行主程序。 通过运行上面的代码,你会看到输出的顺序是"主程序开始" -> "异步任务开始" -> "异步任务开始" -> "异步任务结束" -> "异步任务结束" -> "主程序结束"。可以看到,在执行异步任务时,主程序不会被阻塞,而是继续执行其他任务。 异步编程在处理I/O密集型任务时特别有用,因为它可以充分利用等待I/O操作完成的时间,同时执行其他任务,提高程序的性能和响应性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值