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
相关产品推荐
相关产品推荐

