定义异步函数
import asyncio
async def hello(name: str):
await asyncio.sleep(2)
return f"Hello, {name}!"
导入asyncio
包;
通过 async def
定义异步函数。
手动创建任务
import asyncio
async def hello(name: str):
await asyncio.sleep(2)
return f"Hello, {name}!"
async def main():
# 创建三个任务
task1 = asyncio.create_task(hello("Alice"))
task2 = asyncio.create_task(hello("Bob"))
task3 = asyncio.create_task(hello("Charlie"))
# 等待三个任务完成
Alice_result = await task1
Bob_result = await task2
Charlie_result = await task3
# 打印结果
print(Alice_result, Bob_result, Charlie_result)
if __name__ == "__main__":
# 运行异步程序
asyncio.run(main())
通过asyncio.create_task()创建的任务返回是一个可执行函数,需要使用await获得返回的值。
自动创建任务
使用asyncio.gather()
创建自动任务
import asyncio
async def hello(name: str):
await asyncio.sleep(2)
return f"Hello, {name}!"
async def main():
# 创建三个任务
results = await asyncio.gather(hello("Alice"), hello("Bob"), hello("Charlie"))
print(results)
if __name__ == "__main__":
# 运行异步程序
asyncio.run(main())
使用asyncio.as_completed()
创建自动任务
import asyncio
async def hello(name: str):
await asyncio.sleep(2)
return f"Hello, {name}!"
async def main():
# 创建三个任务
results = asyncio.as_completed([hello("Alice"), hello("Bob"), hello("Charlie")])
for result in results:
print(await result)
if __name__ == "__main__":
# 运行异步程序
asyncio.run(main())
asyncio.gather 与 asyncio.as_completed 的区别
1. asyncio.gather
-
作用:并发运行多个协程,并一次性收集所有结果。
-
特点:
- 会按传入顺序返回结果(即使某些任务先完成,也不会提前返回)。
- 如果有一个任务报错,
gather
会立刻抛异常,默认会取消其它未完成任务(可通过参数return_exceptions=True
来改变)。
-
适用场景:
当你需要 一次性得到所有结果,比如并发请求多个 API,最后要拼装数据。
-
asyncio.as_completed
-
作用:返回一个迭代器,谁先完成就先产出结果。
-
特点:
- 结果顺序取决于完成的先后。
- 更适合 流式处理:一个任务完成了就立刻消费它的结果,而不是等所有任务结束。
-
适用场景:
当你想 边跑边处理,比如爬虫中哪个网页先返回就先处理,提升吞吐效率。核心区别总结
特性 asyncio.gather
asyncio.as_completed
返回结果顺序 按传入任务顺序 按任务完成顺序 返回方式 一次性返回所有结果 一个个产出结果 错误处理 默认立即抛出并取消其它任务 某个任务报错时只影响它本身 适用场景 需要所有结果统一处理 需要流式处理、谁先来先处理
await 的作用
暂停当前协程,等待一个“可等待对象”的结果返回,然后继续往下执行。
1. 可等待对象(awaitable)
能被 await
的对象必须是 awaitable,常见有三类:
- 协程对象(由
async def
函数调用返回的对象) - Task 对象(
asyncio.create_task()
返回的任务) - Future 对象(底层异步结果容器)
2. 工作原理
当执行到 await expr
:
expr
必须是一个 awaitable。- 当前协程会 挂起(yield control),把执行权交给事件循环。
- 事件循环可以在此期间去运行别的任务,提高并发效率。
- 当
expr
对象完成(返回值或异常),事件循环会恢复该协程,并把结果赋给await
表达式。
3. 直观类比
-
await
≈ 同步代码里的result = func()
,但不会阻塞整个线程,只是 挂起当前协程。 -
好比在餐厅点菜:
-
await
就是“我点了菜,等它做好再吃”。 -
服务员(事件循环)可以在你等菜时去服务别的客人(运行其它协程)。
-
将普通函数变为异步函数
使用asyncio.to_thread()
将普通函数创建为异步任务
会把一个同步阻塞函数丢到一个线程池里执行,并返回一个可 await
的结果。
也就是说:
- 在 async 环境里,有些第三方库或者函数(比如
time.sleep()
、文件 I/O、CPU 计算)是同步阻塞的,直接调用会卡住事件循环,影响并发。 - 用
asyncio.to_thread
包装后,就能把它放进后台线程运行,不会阻塞事件循环。
import asyncio
import time
def hello(name: str):
time.sleep(2)
return f"Hello, {name}!"
async def main():
# 创建三个任务
results = asyncio.as_completed([asyncio.to_thread(hello, "Alice!"),
asyncio.to_thread(hello, "Bob!"),
asyncio.to_thread(hello, "Charlie!")])
for result in results:
print(await result)
if __name__ == "__main__":
# 运行异步程序
start = time.time()
asyncio.run(main())
end = time.time()-start
print(f"Time taken: {end:.2f} seconds")