Python 异步编程 async + await + future 用法
一、了解异步编程之前,先来看一个非异步编程的例子,如下所示:
import time
def task1():
time.sleep(5) # 模拟一个耗时 5 秒的 I/O 操作
return 10
def task2():
time.sleep(3) # 模拟一个耗时 3 秒的 I/O 操作
return 20
def main():
result = task1()
print('任务1执行结果:', result)
result = task2()
print('任务2执行结果:', result)
if __name__ == '__main__':
start = time.time()
main()
print('总耗时:', time.time() - start)

二、将上面的代码改造为异步代码(旧)
# 它的工作流程大致是这样的:
# 首先,创建一个事件循环
# 然后,将需要执行的任务注册到事件循环中
# 最后,启动事件循环,开始调度和执行各个任务
import time
import asyncio
# 表示 task1 任务是由事件循环来管理调度,并不是由我们来调度执行
async def task1():
print('task1 开始')
# await 告诉事件循环此处可以挂起,等待 await 后面的对象执行完毕了再向下执行
# await 后面的对象需要是一个使用 async def 定义对象
# time.sleep 需要替换为 async def 定义版本的 sleep 函数
# await time.sleep(5) # 模拟一个耗时 5 秒的 I/O 操作
await asyncio.sleep(5)
print('task1 结束')
return 10
async def task2():
print('task2 开始')
# time.sleep(3) # 模拟一个耗时 3 秒的 I/O 操作
await asyncio.sleep(3)
print('task2 结束')
return 20
async def main():
print('main 开始')
# 获得事件循环
event_loop = asyncio.get_running_loop()
# 手动注册任务
t1 = event_loop.create_task(task1())
t2 = event_loop.create_task(task2())
# 等待 t1 任务执行结束,并且获得 t1 任务的执行结果
result = await t1
print('result:', result)
result = await t2
print('result:', result)
print('main 结束')
if __name__ == '__main__':
start = time.time()
# 创建事件循环
event_loop = asyncio.get_event_loop()
# 启动事件循环
event_loop.run_until_complete(main())
print('总耗时:', time.time() - start)

运行结果中有报错,如上所示。
报错原因说明
asyncio.get_event_loop() 报警告的根源:Python3.7+ 脚本主线程默认没有预创建全局事件循环,直接调用会触发 DeprecationWarning。代码修复如下:# 方案 1:手动新建循环 + 绑定到当前上下文(推荐,最小改动你的代码)
# 在 get_event_loop() 之前先判断有无循环,没有就手动创建并设置,完整可运行修复代码:
import time
import asyncio
async def task1():
print('task1 开始')
await asyncio.sleep(5)
print('task1 结束')
return 10
async def task2():
print('task2 开始')
await asyncio.sleep(3)
print('task2 结束')
return 20
async def main():
print('main 开始')
event_loop = asyncio.get_running_loop()
t1 = event_loop.create_task(task1())
t2 = event_loop.create_task(task2())
result = await t1
print('result:', result)
result = await t2
print('result:', result)
print('main 结束')
if __name__ == '__main__':
start = time.time()
# 修复:不存在循环则手动创建并设置全局循环
try:
event_loop = asyncio.get_event_loop()
except RuntimeError:
# 当前线程无循环,新建并绑定
event_loop = asyncio.new_event_loop()
asyncio.set_event_loop(event_loop)
event_loop.run_until_complete(main())
# 可选:运行完毕关闭循环释放资源
event_loop.close()
print('总耗时:', time.time() - start)
![image]()
修复了还是有报错警示出现,查询豆包得知:
- 报错警告原因
Python3.7 起废弃 “主线程自动生成全局循环” 机制,直接调用
get_event_loop()检测不到已存在的循环,这是个已经抛弃的API,只要触发就会抛出 DeprecationWarning。
asyncio.new_event_loop():手动创建全新事件循环实例asyncio.set_event_loop(loop):把新建的循环绑定到当前线程,后续get_event_loop()就能正常获取,不再报警告
- 注意事项
- 代码末尾务必加上
loop.close(),否则会有资源泄漏警告; - 官方不推荐这种写法,仅适配老旧项目必须兼容旧接口的场景,新项目优先使用
asyncio.run(); get_running_loop()只能在async def协程内部调用,主线程同步代码中不能使用。
所以,再次修复代码如下:
<坚持手动管理 loop(兼容旧写法,消除警告)>
# 3. 如果你坚持手动管理 loop(兼容旧写法,消除警告)
# 不想用 asyncio.run(),改造原来的循环获取逻辑,避开废弃 API:
# 它的工作流程大致是这样的:
# 首先,创建一个事件循环
# 然后,将需要执行的任务注册到事件循环中
# 最后,启动事件循环,开始调度和执行各个任务
import time
import asyncio
# 表示 task1 任务是由事件循环来管理调度,并不是由我们来调度执行
async def task1():
print('task1 开始')
# await 告诉事件循环此处可以挂起,等待 await 后面的对象执行完毕了再向下执行
# await 后面的对象需要是一个使用 async def 定义对象
# time.sleep 需要替换为 async def 定义版本的 sleep 函数
# await time.sleep(5) # 模拟一个耗时 5 秒的 I/O 操作
await asyncio.sleep(5)
print('task1 结束')
return 10
async def task2():
print('task2 开始')
# time.sleep(3) # 模拟一个耗时 3 秒的 I/O 操作
await asyncio.sleep(3)
print('task2 结束')
return 20
async def main():
print('main 开始')
# 获得事件循环
event_loop = asyncio.get_running_loop()
# 手动注册任务
t1 = event_loop.create_task(task1())
t2 = event_loop.create_task(task2())
# 等待 t1 任务执行结束,并且获得 t1 任务的执行结果
result = await t1
print('result:', result)
result = await t2
print('result:', result)
print('main 结束')
if __name__ == '__main__':
start = time.time()
# 替代get_event_loop(),无警告写法
event_loop = asyncio.new_event_loop()
asyncio.set_event_loop(event_loop)
event_loop.run_until_complete(main())
event_loop.close()
print('总耗时:', time.time() - start)
注:以上代码直接弃用get_event_loop()
三、异步代码(新)
import time
import asyncio
async def task1():
print('task1 开始')
await asyncio.sleep(5)
print('task1 结束')
return 10
async def task2():
print('task2 开始')
await asyncio.sleep(3)
print('task2 结束')
return 20
async def main():
print('main 开始')
# 协程内部获取当前正在运行的循环(保留你原有逻辑)
event_loop = asyncio.get_running_loop()
t1 = event_loop.create_task(task1())
t2 = event_loop.create_task(task2())
result = await t1
print('result:', result)
result = await t2
print('result:', result)
print('main 结束')
if __name__ == '__main__':
start = time.time()
# 新标准写法:自动创建、管理、关闭事件循环,无 DeprecationWarning
asyncio.run(main())
print('总耗时:', time.time() - start)

从运行结果来看,没有问题。
下面,移除手动获取 loop,直接用顶层 asyncio.create_task(),内置自动拿当前循环:
import time
import asyncio
# 移除手动获取 loop,直接用顶层 asyncio.create_task(),内置自动拿当前循环
async def task1():
print('task1 开始')
await asyncio.sleep(5)
print('task1 结束')
return 10
async def task2():
print('task2 开始')
await asyncio.sleep(3)
print('task2 结束')
return 20
async def main():
print('main 开始')
# 协程内部获取当前正在运行的循环(保留你原有逻辑)
# event_loop = asyncio.get_running_loop()
# t1 = event_loop.create_task(task1())
# t2 = event_loop.create_task(task2())
t1 = asyncio.create_task(task1())
t2 = asyncio.create_task(task2())
result = await t1
print('result:', result)
result = await t2
print('result:', result)
print('main 结束')
if __name__ == '__main__':
start = time.time()
# 新标准写法:自动创建、管理、关闭事件循环,无 DeprecationWarning
asyncio.run(main())
print('总耗时:', time.time() - start)
对上面代码再优化,进阶极致简化(推荐,asyncio.gather 并行收集结果)
适合多个并发任务、一次性拿所有返回值,代码更短:
# 进阶极致简化(推荐,asyncio.gather 并行收集结果)
import time
import asyncio
async def task1():
print("task1 开始")
await asyncio.sleep(5)
print("task1 结束")
return 10
async def task2():
print("task2 开始")
await asyncio.sleep(3)
print("task2 结束")
return 20
async def main():
print("main 开始")
# gather自动并发执行,一次性收集所有返回值
res1, res2 = await asyncio.gather(task1(), task2())
print(f"result: {res1}")
print(f"result: {res2}")
print("main 结束")
if __name__ == "__main__":
start = time.time()
asyncio.run(main())
print(f"总耗时: {time.time() - start:.3f}")
两者区别
create_task手动创建任务,可中途单独 await、单独取消某个任务,灵活性更高;asyncio.gather内部自动封装create_task,一行启动全部协程并打包返回结果,代码极简,适合批量并行场景。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
【Future用法】
在 Python 的异步编程(
asyncio)中,Future 是一个非常核心的概念。你可以把它想象成一张“提货单”:当你在网店下单购买一件暂时缺货的商品时,商家会给你一张提货单,承诺将来商品到货后会交给你。Future 就是这样一个承诺对象,它代表了一个尚未完成的异步操作的最终结果。以下是
Future 的核心用法和关键特性:1. 核心状态与基本操作
Future 对象有三种状态:等待中(pending)、已完成(done)和已取消(cancelled)。你可以通过以下核心 API 来管理它的状态:set_result(value):标记 Future 已完成,并设置其结果。这会唤醒所有正在等待它的协程。set_exception(exc):标记 Future 因异常而完成。done():检查 Future 是否已经完成(有结果或发生异常)。result():获取结果。如果 Future 尚未完成,调用此方法会抛出InvalidStateError。cancel():取消 Future 的执行。add_done_callback(cb):添加一个回调函数,当 Future 完成时自动执行。
基础示例:手动创建 Future
示例 1:手动 set_result + await
import asyncio
async def demo_future():
# 1. 创建空 Future
fut = asyncio.Future()
# 2. 模拟异步逻辑:延迟2秒后设置结果
async def set_fut_value():
await asyncio.sleep(2)
fut.set_result("操作完成,返回数据:123")
# 后台执行赋值逻辑
asyncio.create_task(set_fut_value())
# 3. await 等待 future 完成
print("等待结果...")
res = await fut
print(res)
print(f"future 是否完成:{fut.done()}")
asyncio.run(demo_future())

示例 2:Future 设置异常
import asyncio
async def future_exception():
fut = asyncio.Future()
async def raise_err():
await asyncio.sleep(1)
fut.set_exception(ValueError("参数非法"))
asyncio.create_task(raise_err())
try:
await fut
except ValueError as e:
print("捕获异常:", e)
print("fut.exception():", fut.exception())
asyncio.run(future_exception())


浙公网安备 33010602011771号