You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将阻塞函数转为Python可等待函数?两种实现方案孰优孰劣?

问题描述

我有如下阻塞函数:

def blocking_function():
    doing.start_preparing()
    while not doing.is_ready():
        pass
    return doing.do()

我希望将其改造为异步函数,查阅asyncio源码后找到了两种实现方案:

import types
import typing
import asyncio

@types.coroutine
def yield_option() -> typing.Generator:
    doing.start_preparing()
    while not doing.is_ready():
        yield
    return doing.do()

def future_and_call_soon_option() -> asyncio.Future:
    doing.start_preparing()
    loop = asyncio.get_running_loop()
    future = loop.create_future()
    def inner():
        if not doing.is_ready():
            loop.call_soon(inner)
        else:
            future.set_result(doing.do())
    inner()
    return future

测试代码如下:

async def main():
    await asyncio.gather(yield_option(), future_and_call_soon_option()) # 并发运行

asyncio.run(main())

两种方案都能正常运行,但哪种方案更优?是否存在更合适的第三种方案?


方案分析与优化建议

两种现有方案的优劣对比

1. yield_option 方案

  • 优势:代码简洁直观,逻辑和原始阻塞函数高度对齐,容易理解。借助生成器协程的yield关键字主动让出CPU,无需手动管理Future对象,适配asyncio事件循环的方式很直接。
  • 劣势:属于忙等待轮询,每次yield都会触发事件循环调度,在准备过程较长时会频繁占用CPU资源,产生不必要的性能开销。

2. future_and_call_soon_option 方案

  • 优势:通过loop.call_soon()递归调度,相比生成器方案减少了协程切换的额外开销,性能略优。
  • 劣势:代码繁琐,需要手动创建、维护Future对象,递归调用的写法可读性差。本质还是忙等待轮询,同样存在CPU资源浪费的问题。

更优的第三种方案

针对上述方案的轮询痛点,有两种更合理的优化方向:

方向1:加入休眠的异步轮询

如果doing的准备过程不需要毫秒级的响应精度,可在轮询间隙加入短暂休眠,大幅降低CPU占用。代码符合现代async/await语法,可读性强:

async def async_sleep_option():
    doing.start_preparing()
    while not doing.is_ready():
        await asyncio.sleep(0.01)  # 休眠10毫秒,可根据需求调整间隔
    return doing.do()

这种方案通过asyncio.sleep()主动让出CPU,仅在指定时间后再检查状态,既保证了异步协作,又避免了无意义的忙等待。

方向2:基于回调的异步通知(最优)

如果doing的API支持回调机制(即准备完成时触发指定函数),可以完全抛弃轮询,实现真正的异步通知:

def callback_based_option() -> asyncio.Future:
    loop = asyncio.get_running_loop()
    future = loop.create_future()
    def on_ready():
        future.set_result(doing.do())
    # 假设doing.start_preparing支持传入回调参数,准备完成时调用on_ready
    doing.start_preparing(on_ready=on_ready)
    return future

这种方案几乎没有CPU开销,只有当准备完成时才会触发后续逻辑,是性能最优的实现方式,但依赖doing提供回调支持。

内容的提问来源于stack exchange,提问作者Alexey_Chebotarev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 19:25:29