网络通信与并发编程(九)asyncio
asyncio
文章目录
- asyncio
- 一、asyncio的基本使用
- 二、asyncio的并发
- 三、异步迭代器于异步上下文管理器
- 3.1异步迭代器
- 3.2异步上下文管理器
- 四、使用asyncio异步访问百度
一、asyncio的基本使用
asyncio模块是python的异步协程模块,使用该模块时python解释器的版本应大于等于3.5。
使用asyncio实现循环调用协程函数:
import asyncio#协程函数是协程中需要执行的任务,需要使用如下的定义方式
async def f():print('协程函数的代码')def main():res=f()#执行协程函数需要采用以下的循环调用方式asyncio.get_event_loop().run_until_complete(res)#或者可以写为,但是解释器版本需大于等于3.7#asyncio.run(res)main()
await的使用方法:
await函数只能用于协程函数内部,当协程函数遇到I/O操作时使用await可以执行其他并发任务,此时该协程任务会被挂起,当I/O执行完以后再继续执行该协程任务。
另外需要注意的是await只能等待非阻塞的任务,例如想要使用await完成爬虫任务需要将常使用的requests库替换为aiohttp库,因为requests的I/O操作会阻塞线程从而协程无法完成并发操作,而aiohttp允许异步爬取多个网站信息。
import asyncioasync def other_task(n):print('start%s'%n)#模拟I/O操作,在此期间协程会执行并发的其他任务await asyncio.sleep(n)print('end%s'%n)async def f():#并发执行两个协程函数#asyncio.gather返回执行协程函数返回值的列表await asyncio.gather(other_task(4),other_task(6))def main():asyncio.run(f())main()
二、asyncio的并发
asyncio并发执行任务有两个函数asyncio.gather和asyncio.wait(python3.11版本后弃用,可以改用asyncio.TaskGroup()类)
asyncio.gather(*aws,return_exceptions)有两个参数,*aws可传入多个需执行的任务;return_exceptions默认为False,此时并发任务报错会直接中断其他任务并抛出错误,return_exceptions=True时并发任务报错其他任务正常执行,报错信息传入返回值中。
asyncio.wait(aws,timeout,return_when),aws传入一个可迭代对象,包含要执行的协程任务;
timeout=None是一个可选参数,用于指定等待的最长时间(以秒为单位)。如果超时时间到达而任务还未完成,则协程函数会取消。如果设置为 None,则会等待直到所有任务完成;return_when=ALL_COMPLETED:这是一个可选参数,用于指定何时返回。它必须为以下常数之一:
- asyncio.FIRST_COMPLETED:如果有任何一个任务完成,则立即返回。
- asyncio.FIRST_EXCEPTION:如果有任何一个任务抛出异常,则立即返回。
- asyncio.ALL_COMPLETED:只有当所有任务都完成时才返回。
asyncio.wait 返回一个集合:(done, pending)。done 包含所有已完成或抛出异常的任务,而 pending 包含所有未完成的任务。
import asyncioasync def other_task(n):print('start%s'%n)#模拟I/O操作,在此期间协程会执行并发的其他任务await asyncio.sleep(n)print('end%s'%n)async def f():#并发执行两个协程函数#asyncio.gather返回执行协程函数返回值的列表done,pending=await asyncio.wait([asyncio.create_task(other_task(6)),asyncio.create_task(other_task(4))],timeout=5)print(done)print(pending)def main():asyncio.run(f())main()
三、异步迭代器于异步上下文管理器
3.1异步迭代器
异步迭代器与一般的迭代器使用方法相同,异步迭代器对象需要包含__ater__()和__anext__(),而async for可以用来遍历异步可迭代对象。
import asyncioclass Read():def __init__(self):self.c=0self.stop=Falseasync def add(self):self.c+=1if self.c>100:self.stop=Truereturn self.cdef __aiter__(self):return selfasync def __anext__(self):val=await self.add()if not self.stop:return valraise StopAsyncIterationasync def main():async for i in Read():print(i)asyncio.run(main())
3.2异步上下文管理器
async with的用法和with相同,async with可以与asyncontextmanager装饰器连用,使用async with时会调用__aenter__()方法,退出async with代码块时会调用__aexit__()方法
from contextlib import asynccontextmanager
import asyncio@asynccontextmanager
#进入with块时代码运行至yield处,退出with块时运行yield后代码
async def t(i):print('enter')await asyncio.sleep(i)yield 'done'print('exit')async def main():async with t(1) as f:print(f)asyncio.run(main())
import asyncioclass MytTest:def __init__(self,n):self.n=nasync def do(self):await asyncio.sleep(self.n)return 'done'async def __aenter__(self):print('enter')#with块的返回值mreturn selfasync def __aexit__(self,exc_type,exc_value,exc_tb):print('exit')await asyncio.sleep(self.n)async def main():async with MytTest(2) as m:res=await m.do()print(res)asyncio.run(main())
四、使用asyncio异步访问百度
import asyncio,aiohttpasync def connect(session,n,url):async with session.get(url=url) as response:if response.status==200:print('%s连接成功'%n)else:print('连接失败')async def main():l = []async with aiohttp.ClientSession() as session:for i in range(100):l.append(asyncio.create_task(connect(session,i, 'https://www.baidu.com'))) # 修正这里await asyncio.gather(*l)loop = asyncio.get_event_loop()
loop.run_until_complete(main())