1. 协程
在定义函数的时候在前面加上 async 修饰,在耗时任务那行代码使用 await 修饰,这时候调用函数,它就会返回一个协程(coroutine)对象,然后调用 asyncio.run() 把协程对象丢进去就能执行了
import asyncio import time async def test(i, n): print(f"任务{i}将休眠{n}秒") await asyncio.sleep(n) print(f"任务{i}在{n}秒后继续执行") start_time = time.time() asyncio.run(test(1, 1)) print(f'总用时{time.time() - start_time}秒')
执行输出如下
任务1将休眠1秒 任务1在1秒后继续执行 总用时1.0107183456420898秒
2. 使用 asyncio 执行多任务
2.1 同步执行
假设有一个任务 test(),需要执行三次,每次分别休眠 2 秒、1 秒、3 秒,如果是同步执行,一共需要耗时 6 秒
import time def test(i, n): print(f"任务{i}将休眠{n}秒") time.sleep(n) print(f"任务{i}在{n}秒后继续执行") start_time = time.time() task_list = [test(1, 2), test(2, 1), test(3, 3)] print(f'总用时{time.time() - start_time}秒')
执行输出如下
任务1将休眠2秒 任务1在2秒后继续执行 任务2将休眠1秒 任务2在1秒后继续执行 任务3将休眠3秒 任务3在3秒后继续执行 总用时6.022886514663696秒
2.2 异步执行
asyncio 把上面的多任务改成异步执行,就是使用 async 和 await 修饰一下。在调用执行的时候,需要创建一个事件循环,在事件循环里执行任务,这样它会在耗时的时候自动切换到其他任务。另外,最后要记得关闭事件循环。具体写法如下
import asyncio import time async def test(i, n): print(f"任务{i}将休眠{n}秒") await asyncio.sleep(n) print(f"任务{i}在{n}秒后继续执行") start_time = time.time() task_list = [test(1, 2), test(2, 1), test(3, 3)] loop = asyncio.get_event_loop() try: loop.run_until_complete(asyncio.wait(task_list)) finally: loop.close() print(f'总用时{time.time() - start_time}秒')
执行输出如下
任务2将休眠1秒 任务3将休眠3秒 任务1将休眠2秒 任务2在1秒后继续执行 任务1在2秒后继续执行 任务3在3秒后继续执行 总用时2.9994614124298096秒
同步执行的时候需要 6 秒,改为异步执行需要 3 秒就行了
2.3 异步执行的返回值
如果想要得到异步任务的返回值,只需要使用 asyncio.ensure_future() 函数把任务包装成一个 Task 对象,执行完之后再调用它的 result() 方法就能得到返回值了
import asyncio import time async def test(i, n): print(f"任务{i}将休眠{n}秒") await asyncio.sleep(n) print(f"任务{i}在{n}秒后继续执行") return f"任务{i}执行{n}秒后结束" start_time = time.time() task_list = [asyncio.ensure_future(test(1, 2)), asyncio.ensure_future(test(2, 1)), asyncio.ensure_future(test(3, 3))] loop = asyncio.get_event_loop() try: loop.run_until_complete(asyncio.wait(task_list)) finally: loop.close() task_result = [task.result() for task in task_list] print(f"task_result: {task_result}") print(f'总用时{time.time() - start_time}秒')
执行输出如下
任务1将休眠2秒 任务2将休眠1秒 任务3将休眠3秒 任务2在1秒后继续执行 任务1在2秒后继续执行 任务3在3秒后继续执行 task_result: ['任务1执行2秒后结束', '任务2执行1秒后结束', '任务3执行3秒后结束'] 总用时3.0089805126190186秒
3. 举例:异步下载图片
如果是一张一张按顺序下载图片,我这里大概需要一分多钟下载完 39 张图片,改为 asyncio 异步下载大概需要三四秒。
注意,发起异步网络请求不能使用 requests,因为它是同步的,而应该 aiohttp 或 httpx,我这里使用的是 aiohttp
import asyncio import os.path import time import aiohttp import requests def get_img_url(): img_url_list = list() res = requests.get("https://game.gtimg.cn/images/lol/act/img/js/hero/1.js").json() for skin in res.get("skins"): if skin["mainImg"]: img_url_list.append(skin["mainImg"]) elif skin["iconImg"]: img_url_list.append(skin["iconImg"]) elif skin["sourceImg"]: img_url_list.append(skin["sourceImg"]) elif skin["chromaImg"]: img_url_list.append(skin["chromaImg"]) return img_url_list async def download_img(img_url): save_path = "./图片" if not os.path.exists(save_path): os.mkdir(save_path) name = os.path.split(img_url)[1] async with aiohttp.ClientSession() as session: async with session.get(img_url) as res: with open(os.path.join(save_path, name), "wb") as f: f.write(await res.content.read()) if __name__ == '__main__': url_list = get_img_url() print(len(url_list), url_list) start_time = time.time() task_list = [download_img(url) for url in url_list] loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(task_list)) print("spent_time:", time.time() - start_time)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战