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

asyncio.gather出现“Future for request id x is already done”提示的原因、影响及代码优化咨询

asyncio.gather出现“Future for request id x is already done”提示的原因、影响及代码优化咨询

先直接回应你的几个问题,再给出具体的优化建议:

1. 关于问题原因的假设

你的初始猜测不对,这个提示不是来自asyncio.gather,而是你使用的OPC UA客户端库内部的futures管理逻辑发出的。asyncio.gather本身不会输出这种带“request id”的提示,而且你后来也通过排查确认了来源是OPC UA库——这类提示通常是库内部处理请求时,重复操作了已经完成的future对象导致的。

2. 提示的影响

从你描述的“没有堆栈跟踪”来看,这应该只是库内部的日志提示,不是未捕获的异常,你的程序大概率不会因为这个提示而停止运行。不过保险起见,你可以留意每次出现这个提示后,对应任务的返回结果是否正常、有没有数据丢失的情况,确保业务逻辑不受影响。

3. 代码优化建议

针对这个问题,结合你的代码,建议从以下几个方向调整:

(1)确保客户端连接正确关闭

你的代码里await client.disconnect()只放在了try块的成功路径里,一旦发生异常(比如TimeoutError),连接可能没有被正确关闭,残留的连接或future可能会导致库内部出现异常提示。把disconnect放到finally块里,确保无论成功还是失败都能清理连接:

async def get_daten(url, uri, maschine):
    client = Client(url=url)
    client.session_timeout = 30000
    stueckzahl = None
    zeitpunkt = datetime.now()
    try:
        await client.connect()
        idx = await client.register_namespace(uri)
        stueckzahl_node = client.get_node(f"ns={idx};i=7")
        stueckzahl = await client.get_values([stueckzahl_node])
        stueckzahl = stueckzahl[0]
    except OSError as e:
        # 可添加日志记录异常详情
        pass
    except asyncio.exceptions.TimeoutError as e:
        print(f"TimeoutError: {maschine}, {url}")
    except asyncio.exceptions.CancelledError as e:
        print(f"CancelledError: {maschine}, {url}")
    except BadNotWritable as e:
        print(f"BadNotWritable: {maschine}, {url}, {e}")
        print(await client.get_namespace_array())
    finally:
        # 确保连接被关闭,即使断开时出错也记录日志
        try:
            await client.disconnect()
        except Exception as disconnect_err:
            print(f"Failed to disconnect from {maschine}: {disconnect_err}")
    return zeitpunkt, stueckzahl

(2)升级OPC UA库版本

这类内部future管理的问题,很可能是库的已知bug,建议检查你使用的OPC UA库的官方issue或更新日志,看看是否有相关问题的修复,升级到最新的稳定版本,大概率能解决这个偶发提示。

(3)替换print为日志模块,方便排查

把代码中的print换成Python标准库的logging模块,这样可以记录更详细的上下文(比如时间、日志级别),方便你追踪这个提示出现时的业务场景:

import logging
# 配置日志格式
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)

# 示例:替换原有的print
logger.error(f"TimeoutError: {maschine}, {url}")

(4)检查客户端连接复用可能性

你的代码每次调用get_daten都创建一个新的Client实例,如果你的程序是每分钟运行一次,每次创建大量短连接,可能会给服务器和客户端库带来额外压力。可以查看OPC UA库的文档,确认是否支持协程安全的连接复用,或者使用连接池来减少连接创建销毁的开销,这也能降低库内部出现异常提示的概率。

最后再强调下:你的asyncio.gather用法是完全正确的,它不会因为任务提前完成而出现问题,不用调整这部分逻辑。

备注:内容来源于stack exchange,提问作者Jens Neumann

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 14:58:03