当前位置: 首页 > 工具软件 > Emscripten > 使用案例 >

emscripten与线程

袁耀
2023-12-01

一、首先要让示例跑起来

运行以下命令,编译一个简单的示例:

em++ tests/worker_api_worker.cpp -s BUILD_AS_WORKER=1 -s EXPORTED_FUNCTIONS="['_one']" -o worker.js
em++ tests/worker_api_main.cpp -o main.html
浏览器打开main.html,应该可以看到结果了。

备注:所有示例的编译方法,可以参见 emscripten\tests\runner.py 文件。


二、emscripten_call_worker的参数

调用worker的方法通过这个emscripten_call_worker函数实现,看看它的参数定义:

/*
* Asynchronously call a worker.
*
* The worker function will be called with two parameters: a
* data pointer, and a size. The data block defined by the
* pointer and size exists only during the callback and
* _cannot_ be relied upon afterwards - if you need to keep some
* of that information around, you need to copy it to a safe
* location.
*
* The called worker function can return data, by calling
* emscripten_worker_respond(). If called, and if a callback was
* given, then the callback will be called with three arguments:
* a data pointer, a size, and * an argument that was provided
* when calling emscripten_call_worker (to more easily associate
* callbacks to calls). The data block defined by the data pointer
* and size behave like the data block in the worker function -
* it exists only during the callback.
*
* @funcname the name of the function in the worker. The function
* must be a C function (so no C++ name mangling), and
* must be exported (EXPORTED_FUNCTIONS).
* @data the address of a block of memory to copy over
* @size the size of the block of memory
* @callback the callback with the response (can be null)
* @arg an argument to be passed to the callback
*/
void emscripten_call_worker(worker_handle worker, const char *funcname, char *data, int size, void (*callback)(char *, int, void*), void *arg);
参数说明:

worker -- 句柄

funcname -- worker里的函数名称,C风格,并且使用EXPORTED_FUNCTIONS导出

data -- 数据地址

size -- 数据大小

callback -- 回调函数(当异步执行完成时,回调这个函数,同时会把data 和size 返回,但是不要期望此data地址还是传入的data地址)

arg -- 回调函数的参数

令人郁闷的地方:

(1)传入的data 和 size,是传值,而不是指针和引用,因为emscripten是将data复制一份,发给worker

(2)callback收到的data,仍然是传值,是数据的拷贝

(3)所以不要期望主程序与worker之间能够共享指针。

三、一点点体会

worker就是一个线程,拥有自己的消息队列

1、emscripten_create_worker = 在线程池中创建了一个线程

2、emscripten_call_worker = 主程序发送消息给线程(在js中,实际体现在onmessage函数里)

onmessage = function(msg) {
  //确定消息函数
  var func = Module['_' + msg.data['funcName']];
  if (!func) throw 'invalid worker function to call: ' + msg.data['funcName'];
  var data = msg.data['data'];
  if (data) {
    //复制传入的参数数据
    if (!data.byteLength) data = new Uint8Array(data);
    if (!buffer || bufferSize < data.length) {
      if (buffer) _free(buffer);
      bufferSize = data.length;
      buffer = _malloc(data.length);
    }
    HEAPU8.set(data, buffer);
  }
  inWorkerCall = true;
  workerResponded = false;
  workerCallbackId = msg.data['callbackId'];
  if (data) {
    //调用相应的消息函数
    func(buffer, data.length);
  } else {
    func(0, 0);
  }
  inWorkerCall = false;
}
3、emscripten_worker_respond = 线程发送消息给主程序

四、线程交互的麻烦

1、web worker里面无法调用webgl

2、worker里生成图片,再传回主函数

3、线程如何暂停(不是terminate)?线程工作时是不响应onmessage的。

找到一个解决方案:http://msmvps.com/blogs/theproblemsolver/archive/2012/05/02/html5-background-tasks-using-web-workers.aspx

还有一个示例:http://demos.html5support.nl/WebWorkers/Chunked

Using  a chunking algorithm

The solution is to use a chunking algorithm. This algorithm brakes the calculation into different groups and use the setTimeout() API to execute the next chunk after a small delay. The result of using setTimeout() is that the message posted can be read and we can actually pause and resume the worker execution. Using a chunking algorithm out background worker JavaScript looks like this:


====== 未完 待续 ======

 类似资料: