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)

image

二、将上面的代码改造为异步代码(旧)

# 它的工作流程大致是这样的:
# 首先,创建一个事件循环
# 然后,将需要执行的任务注册到事件循环中
# 最后,启动事件循环,开始调度和执行各个任务
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)

image

运行结果中有报错,如上所示。

报错原因说明

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
修复了还是有报错警示出现,查询豆包得知:
  1. 报错警告原因
     
    Python3.7 起废弃 “主线程自动生成全局循环” 机制,直接调用 get_event_loop() 检测不到已存在的循环,这是个已经抛弃的API,只要触发就会抛出 DeprecationWarning。
  • asyncio.new_event_loop():手动创建全新事件循环实例
  • asyncio.set_event_loop(loop):把新建的循环绑定到当前线程,后续 get_event_loop() 就能正常获取,不再报警告
  1. 注意事项
  • 代码末尾务必加上 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)

image

从运行结果来看,没有问题。

下面,移除手动获取 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}")

两者区别

  1. create_task 手动创建任务,可中途单独 await、单独取消某个任务,灵活性更高;
  2. asyncio.gather 内部自动封装 create_task,一行启动全部协程并打包返回结果,代码极简,适合批量并行场景。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

【Future用法】

在 Python 的异步编程(asyncio)中,Future 是一个非常核心的概念。你可以把它想象成一张“提货单”:当你在网店下单购买一件暂时缺货的商品时,商家会给你一张提货单,承诺将来商品到货后会交给你。Future 就是这样一个承诺对象,它代表了一个尚未完成的异步操作的最终结果2
以下是 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())

image

示例 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())

 

posted @ 2026-07-09 13:38  chenlight  阅读(6)  评论(0)    收藏  举报