一、首先要让示例跑起来
运行以下命令,编译一个简单的示例:
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://demos.html5support.nl/WebWorkers/Chunked
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:
====== 未完 待续 ======