协程并发下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import aiohttp
import asyncio

urls = [
"https://www.baidu.com",
"https://www.baidu.com",
"https://www.baidu.com",
]


async def async_download(url, idx):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
with open(f"{time.time()}.html", "wb") as f:
f.write(await resp.content.read())
print(f"download {idx} success")


async def main():
tasks = [asyncio.create_task(async_download(url, idx)) for idx, url in enumerate(urls)]
await asyncio.gather(*tasks)


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

其他

1.协程的理解

想要实现协程并发下载,需要将对应的 coroutine function 转为 task,然后再将 task 加入到 event loop 中才能实现并发。

2.错误的实现

如果没有将 coroutine function 转为 task,而是直接 await 调用 coroutine function,那么就会变成串行下载,而不是并发下载。

错误示例如下,可以自己测试一下,最终结果是串行下载的

1
2
3
4
async def main():
coroutine_functions = [async_download(url, idx) for idx, url in enumerate(urls)]
for coroutine_function in coroutine_functions:
await coroutine_function

3.怎么将 coroutine function 转为 task

可以去网上搜,方式有很多,代码里的 asyncio.create_task 和 asyncio.gather 都能够将 coroutine function 转为 task。

这里提供一个和错误示例相对应的正确示例

1
2
3
4
async def main():
tasks = [asyncio.create_task(async_download(url, idx)) for idx, url in enumerate(urls)]
for task in tasks:
await task

这里并没有使用 asyncio.gather,而是直接 await task,但是这里已经用 asyncio.create_task 将 coroutine function 转为 task
了,所以效果也是并发下载。