在一个线程中如果遇到IO等待事件,线程不会傻傻等,利用空余时间去做其他事情
事件循环
任务列表 = 【 任务1, 任务2, 任务3, 。。。】
while True:
可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将“可执行”,“已完成”的任务返回
for 就绪任务 in 可执行的任务列表:
执行已就绪的任务
for 已完成的任务 in 已完成的任务列表
在任务列表中移除 已完成的任务
如果 任务列表 中的任务全部都已完成,则终止循环.
import asyncio # 去生成一获取一个事件循环 loop = asyncio.get_event_loop() # 将任务放到“任务列表” loop.run_until_complete(任务)
快速上手
协程函数,定义函数的时候aysnc def 函数名
协程对象,执行携程函数()得到的协程对象
# 协程函数 async def func() pass # 协程对象 result = func()
注意:执行协程函数创建协程对象,函数内部代码不会执行
如果想要执行携程函数内部代码,必须要将协程对象交给事件循环来处理
完整代码如下:
python3.7之前
import asyncio async def fun(): print("hello world") result = fun() loop = asyncio.get_event_loop() loop.run_until_complete(result)
python3.7之后
import asyncio async def fun(): print("hellow world") result = fun() asynicio.run(result)
await
await + 可等待的对象(协程对象、Future、Task对象)
import asyncio async def fun(): print("hello world") response = await asyncio.sleep(2) print("结果", response) asyncio.run(fun())
import asyncio async def others(): print("Start") await asyncio.sleep(2) print("End") return "返回值" async def func(): print("执行携程函数内部代码") # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行,当协程挂起时,事件循环可以去 执行其他协程(任务) response = others() print("IO请求结束,结果为", response) asyncio.run(func())
import asyncio async def others(): print("Start") await asyncio.sleep(2) print("End") return "返回值" async def func(): print("执行携程函数内部代码") # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行,当协程挂起时,事件循环可以去 执行其他协程(任务) response1 = others() print("IO请求结束,结果为", response2) response3 = others() print("IO请求结束,结果为", response3) asyncio.run(func())
await就是等待对象的值得到结果之后再继续往下走
Task对象
在事件循环中添加多个任务的
Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用asyncio.create_task()函数以外,还可以用低层级的loop.create_future()函数或ensure_future()函数,不建议手动实例化Task对象。
注意:asyncio.create_ftask()函数在python3.7中被加入,在python3.7之前,可以改用低层级的asyncio.ensure_future()函数
import asyncio async def func(): print(1) await asyncio.sleep(2) print(2) return "返回值" async def main(): task1 = asyncio.create_task(func()) task2 = asyncio.create_task(func()) print("main结束") # 当执行某协程遇到IO操作时,会自动化切换执行其他任务 # 此处的await是等待相对应的协程全部执行完毕并获取结果 ret1 = await task1 ret2 = await task2 print(ret1, ret2) asyncio.run(main())
常用写法
import asyncio async def func(): print(1) await asyncio.sleep(2) print(2) return "返回值" async def main(): print("main开始") task_list = [ asyncio.create_task(func(), name="n1"), asyncio.create_task(func(), name="n2") ] print("main结束") done, pending = await asyncio.wait(task_list, timeout=None) print(done) asyncio.run(main())
不常用写法
import asyncio async def func(): print(1) await asyncio.sleep(2) print(2) return "返回值" task_list = [ func(), func() ] done, pending = asyncio.run(asyncio.wait(task_list)) print(done)
asyncio.Future对象
Task继承Future对象,Task对象内部await结果的处理基于Future对象来的
async def main(): # 获取当前事件循环 loop = asyncio.get_running_loop() # 创建一个任务(Future对象),这个任务什么都不干 fut = loop.create_future() # 等待任务最终结果(Future对象),没有结果会一直等下去 await fut asyncio.run(main())
解释Task的过程
import asyncio async def set_after(fut): await asyncio.sleep(2) fut.set_result("666") async def main(): # 创建当前事件循环 loop = asyncio.get_running_loop() # 创建一个任务(Future对象),没绑定任何行为,则这个任务永远不知道什么时候结束 fut = loop.create_future() # 创建一个任务(Task对象),绑定了set_after函数,函数内部在2s之后,会给fut赋值 # 即手动设置future任务的最终结果,那么fut就可以结束了 await loop.create_task( set_after() ) # 等待Future对象获取最终结果,否则一致等下去 data = await fut print(data) asyncio.run( main() )
区别于concurrent.futures.Future对象
import time from concurrent.futures import Future from concurret.futures.thread import ThreadPoolExecutor from concurret.futures.process import ProcessPoolExecutor def func(value): time.sleep(1) print(value) return 123 # 创建线程池 pool = ThreadPoolExecutor(max_workers = 5) # 创建进程池 # pool = ProcessPoolExecutor(max_workers = 5) for i in range(10) fut = pool.submit(func, 1) print(fut)
爬虫案例
import asyncio class AsynContextMannager: def __init__: self.conn = conn async def do_something(self): # 异步操作数据库 return 666 async def __aenter__(self): # 异步链接数据库 self.conn = await asyncio.sleep(1) reeturn self async def __aexit(self, exc_type, exc, tb) #异步关闭数据库链接 await asyncio.sleep(1) async def func(): async with AsynContextMannager() as f: result = await f.do_something() print(result) asyncio.run( func() )
异步上下文管理器
此种对象通过定义_aenter_()和_aexit_()方法来对async with语句中的环境进行控制。
import asyncio import requests async def download_image(url): # 发送网路请求,下载图片(遇到网路下载图片的IO请求,自动化切换到其他任务) print("开始下载", url) loop = asyncio.get_event_loop() # requests模块不支持异步操作,所以就使用线程池来配合实现 future = loop.run_in_executor(None, requests.get, url) response = await future print("下载完成") # 图片保存到本地文件 file_name = url.rsplit("-")[-1] with open(file_name, mode="wb") as file_object: file_object.write(response.content) if __name__ == "__main__": url_list = [1,2,3] tasks = [ download_image(url) for url in url_list ] loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks))