如何在Emscripten应用中从主线程调用工作线程处理字体数据?
解决方案与实现示例
问题1:从JavaScript调用C++创建的WASM Worker
要实现主线程JS拿到字体数据后传递给C创建的Worker,需通过Emscripten的绑定机制暴露C函数给JS,同时在C++中处理Worker的消息接收逻辑:
步骤1:C++端代码实现
#include <emscripten.h> #include <emscripten/threading.h> #include <vector> #include <string> // 全局Worker线程ID emscripten_thread_t font_worker = 0; // Worker线程的入口函数 void font_processing_worker(void* arg) { // 循环接收主线程消息 while (true) { emscripten_message_t msg; if (emscripten_receive_message(&msg, -1) == EMSCRIPTEN_RESULT_SUCCESS) { // 解析字体数据(示例传递字体名称,可扩展为二进制数据) std::string font_name(static_cast<char*>(msg.data), msg.numBytes); // 这里添加字体处理逻辑,比如解析字体结构、传递给渲染模块等 printf("Processing font: %s\n", font_name.c_str()); // 释放消息内存 emscripten_free(msg.data); } } } // 暴露给JS的函数:初始化Worker并传递字体数据 extern "C" EMSCRIPTEN_KEEPALIVE void init_font_worker_and_send_data(const char* font_name, const uint8_t* font_data, size_t data_len) { if (!font_worker) { // 创建WASM Worker font_worker = emscripten_malloc_wasm_worker(font_processing_worker, nullptr); } // 打包字体名称(实际场景可传递二进制数据) size_t total_size = strlen(font_name) + 1; void* msg_data = emscripten_malloc(total_size); memcpy(msg_data, font_name, total_size); // 发送消息到Worker emscripten_send_message(font_worker, msg_data, total_size); } EMSCRIPTEN_BINDINGS(font_worker_module) { emscripten_bind_void_func("initFontWorkerAndSendData", &init_font_worker_and_send_data); }
步骤2:JavaScript端调用
主线程获取queryLocalFonts结果后,调用暴露的C++函数:
async function fetchAndSendFonts() { const fonts = await window.queryLocalFonts(); for (const font of fonts) { // 获取字体二进制数据 const fontBlob = await font.blob(); const fontArrayBuffer = await fontBlob.arrayBuffer(); const fontUint8 = new Uint8Array(fontArrayBuffer); // 调用C++暴露的函数,传递字体名称和数据 Module.initFontWorkerAndSendData(font.fullName, fontUint8, fontUint8.length); } } window.onload = () => { fetchAndSendFonts(); };
编译注意事项
编译时需添加线程相关选项:
emcc your_code.cpp -o output.js -pthread -s PROXY_TO_PTHREAD=1 -s EXPORTED_FUNCTIONS='["_init_font_worker_and_send_data"]' -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'
问题2:自定义Web Worker脚本解决self is not defined错误
Emscripten的Worker必须加载其运行时环境,直接写空脚本会导致运行时变量未定义。正确的ProcessLocalFonts.js需先导入Emscripten的Worker运行时,再处理消息:
步骤1:ProcessLocalFonts.js 内容
// 加载Emscripten的Worker运行时(路径需与你的输出文件对应) importScripts('./output.js'); // 等待WASM运行时初始化完成 Module.onRuntimeInitialized = () => { console.log("Font processing worker ready"); // 接收主线程消息 self.onmessage = async (e) => { const { fontName, fontData } = e.data; // 调用WASM中的字体处理函数(需C++提前暴露) Module.ccall('processFont', null, ['string', 'array', 'number'], [fontName, fontData, fontData.length]); }; };
步骤2:主线程JS创建Worker并传递数据
// 初始化Worker const fontWorker = new Worker('./ProcessLocalFonts.js'); async function fetchAndSendFonts() { const fonts = await window.queryLocalFonts(); for (const font of fonts) { const fontBlob = await font.blob(); const fontArrayBuffer = await fontBlob.arrayBuffer(); const fontUint8 = new Uint8Array(fontArrayBuffer); // 发送字体数据到Worker,转移ArrayBuffer所有权避免拷贝 fontWorker.postMessage({ fontName: font.fullName, fontData: fontUint8 }, [fontUint8.buffer]); } } window.onload = () => { fetchAndSendFonts(); };
步骤3:C++端暴露处理函数
#include <emscripten.h> #include <vector> #include <string> extern "C" EMSCRIPTEN_KEEPALIVE void processFont(const char* font_name, const uint8_t* font_data, size_t data_len) { // 字体处理逻辑:比如解析字体文件、提取字形数据等 printf("Processing font in custom worker: %s, data length: %zu\n", font_name, data_len); } EMSCRIPTEN_BINDINGS(font_process_module) { emscripten_bind_void_func("processFont", &processFont); }
编译注意事项
编译时需启用Worker支持:
emcc your_code.cpp -o output.js -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=1 -s EXPORTED_FUNCTIONS='["_processFont"]' -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'
内容的提问来源于stack exchange,提问作者Ashot
相关产品推荐
相关产品推荐

