Cesium是如何实现多线程的

这是我参与更文挑战的第9天,活动详情查看: 更文挑战

Web Worker

背景

众所周知,JavaScript是单线程模型,所有的任务只能在同一条线程上进行完成,前边的任务未完成则后续任务只能等待,所以在H中引入了Web Worker,为JavaScript创建一个多线程的环境,将部分任务提供给它在后台运行,前台后台同时运行。

Web Worker是后台运行的JavaScript,它独立于其他脚本且不会影响页面的性能。引入Web Worker的好处是一些计算密集型或高延迟的任务,被 Web Worker 线程所处理,主线程就会很流畅,不会被阻塞或拖慢,而此时 Web Worker 在后台运行。但是这也正是 Web Worker 比较耗费资源的原因。

浏览器支持及使用

除了IE外所有主流浏览器均支持Web Worker。

可在创建Worker之前检测是否支持

if(typeof(Worker)!=="undefined") {
    // 支持.....
}
else {
    // 不支持..
}
复制代码

Web Worker在一个独立的线程中运行,所以代码需要放在一个单独的文件中。加载时如果存在指定文件,浏览器会在文件下载完毕后执行,生成新的Worker线程,如果加载文件失败不会有任何提示。

创建Worker后利用postMessage()启动

var worker = new Worker('worker.js');
var info = 'start worker!'
worker.postMessage(info);
复制代码

在Worker中使用onmessge事件接收主线程的消息来实现一些操作。

onmessage = function(e) {
    var data = e.data
}
复制代码

同样的,从Worker发消息到主线程也采用同样方法。

// Receive the message from the main thread
onmessage = function(e) {
    var info = e.data;
    var result = info + ' get';
    postMessage(result);
};
复制代码

可以使用addEventListener来替换onmessage

停止Worker有两种方法,在主线程中调用worker.terminate()或在内部调用self.close()均可。在任务结束后一定要停止,因为Worker会一直在后台运行耗费资源,不应该过度使用。

注意事项

  • 主线程与Worker之间传递的消息不是共享的,因为系统将消息对象传递给Worker后会将其序列化,在另一端再取消序列化。大部分浏览器通过JSON的编码解码实现。
  • Worker的self和this都是Worker的全局作用域。
  • Worker无法处理DOM,无法使用window对象、document对象等。
  • Worker可以生成子Worker,但需要注意:子Worker必须和父线程处在相同origin中,其中的URI应相对于父Worker位置解析。

Cesium的异步+多线程

Cesium中涉及到大量三维球计算和大数据量交互,比如三角网,参数化Geometry等,都是在Worker中实现的,参数的传递以及不同类型对应的不同算法。

Cesium源码中Source\Core\TaskProcessor.js内为Cesium封装的Worker。我们简单来看一下。

function TaskProcessor(workerPath, maximumActiveTasks) {
    this._workerPath = new Uri(workerPath).isAbsolute()
        ? workerPath
    : TaskProcessor._workerModulePrefix + workerPath;
    this._maximumActiveTasks = defaultValue(
        maximumActiveTasks,
        Number.POSITIVE_INFINITY
    );
    this._activeTasks = 0;
    this._deferreds = {};
    this._nextID = 0;
}


TaskProcessor.prototype.scheduleTask = function (
parameters,
 transferableObjects
) {
    if (!defined(this._worker)) {
        this._worker = createWorker(this);
    }

    // ……


    return when(canTransferArrayBuffer(), function (canTransferArrayBuffer) {
        // ……

        return deferred.promise;
    });
};
复制代码

我们使用时只需要创建一个TaskProcessor,指定类型,然后调用scheduleTask,接收对应具体参数,然后返回一个Promise对象,我们可以异步的获取的对应结果。

使用方法:

var taskProcessor = new Cesium.TaskProcessor('myWorkerPath');
var promise = taskProcessor.scheduleTask({
    someParameter : true,
    another : 'hello'
});
if (!Cesium.defined(promise)) {
    // too many active tasks - try again later
} else {
    Cesium.when(promise, function(result) {
        // use the result of the task
    });
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享