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

Emscripten并发获取图片报错问题求解

问题描述

使用Emscripten的Fetch API批量获取并处理图片时出错,单张处理正常,多张同时获取时报错:

Uncaught RuntimeError: index out of bounds
Error: Object { name: "ExitStatus", message: "Program terminated with exit(1)", status: 1 }

推测是异步操作的资源共享导致竞争条件、数据不一致。尝试过给每个异步调用分配独立Module,虽然可行但认为不是最优方案。相关代码如下:

function read_object(object, url) {
    return new Promise((resolve, reject) => {
        try {
            Module.onFetchClosed = (succeeded) => {
                if (!succeeded) {
                    reject(new Error("Fetch failed"));
                    return;
                };
                resolve();
            };
            object.read_url(url);
        } catch(e) {
            reject(e);
        };
    });
};

var Module = { onRuntimeInitialized: function() {

    async function async_call(n) {
        try {
            var object = new Module.cpp_object();
            let url = await get_url(n);
            await read_object(object, url);
            ...
        } catch(e) {
            object.delete();
            console.error("Error:", e);
        };
    };

    let promises = [];
    for (let i = 0; i < NUM; i++) {
        let promise = async_call(i);
        promises.push(promise)
        console.log(i);
    };
    Promise.all(promises).then(() => {
        ...;
    }).catch((err) => {
        console.error("Something went wrong:", err);
    });
}};
解决方案

问题核心在于全局的Module.onFetchClosed被多个异步请求覆盖,当多个read_object调用同时执行时,后设置的回调会覆盖前一个,导致最早的请求完成时触发的是最后一个请求的resolve/reject,引发状态混乱和越界错误。

不需要创建多个Module,只需要让每个Fetch请求绑定独立的回调逻辑,避免共享全局回调:

修改后的代码

function read_object(object, url) {
    return new Promise((resolve, reject) => {
        try {
            // 保存原有全局回调,避免覆盖其他请求的逻辑
            const originalOnFetchClosed = Module.onFetchClosed;
            Module.onFetchClosed = (succeeded) => {
                // 执行完当前请求后恢复全局回调
                Module.onFetchClosed = originalOnFetchClosed;
                if (!succeeded) {
                    reject(new Error("Fetch failed"));
                    return;
                }
                resolve();
            };
            object.read_url(url);
        } catch(e) {
            reject(e);
        }
    });
}

var Module = { onRuntimeInitialized: function() {

    async function async_call(n) {
        let object = null;
        try {
            object = new Module.cpp_object();
            let url = await get_url(n);
            await read_object(object, url);
            // 这里添加图片处理逻辑
            ...
        } catch(e) {
            console.error("Error:", e);
        } finally {
            // 确保对象总是被销毁,避免内存泄漏
            if (object) {
                object.delete();
            }
        }
    };

    let promises = [];
    for (let i = 0; i < NUM; i++) {
        promises.push(async_call(i));
        console.log(i);
    };
    Promise.all(promises).then(() => {
        ...;
    }).catch((err) => {
        console.error("Something went wrong:", err);
    });
}};

关键改进点

  • 避免全局回调覆盖:每次调用read_object时,先保存原有的onFetchClosed,执行完当前请求后恢复,确保每个请求的回调独立,不会互相干扰。
  • 完善资源清理:把object.delete()放到finally块中,无论成功或失败都能确保对象被销毁,防止内存泄漏。
  • 变量作用域优化:将object声明移到try外,确保在finally中能访问到。

如果你的Emscripten版本支持,可以考虑使用Emscripten原生的Promise化Fetch API,它不需要手动绑定回调,能从根源避免这类回调覆盖问题:

// 示例:使用原生Promise化Fetch
async function read_object(object, url) {
    try {
        const response = await Emscripten.fetch(url);
        if (!response.ok) {
            throw new Error("Fetch failed");
        }
        // 将响应传递给cpp_object处理
        await object.process_response(response);
    } catch(e) {
        throw e;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:59:51