1. 引言

多线程是编程中常用的方法,例如,在桌面程序中,主线程一般是UI线程,负责UI绘制与用户交互,而运算处理往往是交给背后的工作线程,这样可以有效避免交互时的卡顿感

浏览器是多进程的,每打开一个网页,都会开启一个渲染进程,渲染进程包含:

  • GUI渲染线程 (有且只有一个)
  • JS引擎线程 (有且只有一个)
  • 事件触发线程
  • 定时器触发线程
  • 异步http请求线程

其中,JS引擎线程就是解析JS代码的线程,由于JS引擎线程有且只有一个,所以JS代码执行是单线程的(笔者注:异步函数是使用任务队列实现的,除非调用了其他线程的函数,如定时器等,不然异步函数还是单线程执行的)

GUI渲染线程与JS引擎线程是互斥的,且JS引擎线程会先执行,如果JS代码卡住会导致GUI绘制卡住

有关浏览器架构与原理,可以参考:

Web Workers就是创建JS代码执行的线程,使得JS代码执行能以多线程的方式执行,避免JS引擎线程卡住

有关Web Workers的解释可以参考:

本文描述浏览器中的Web Workers并基于Cesium源码进行举例

2. Web Workers

通常而言,Web Workers包含:

  • 专用线程(Dedicated Workers)
  • 共享线程(Shared Workers)
  • 后台线程(Service Workers)等

这里主要是记述通常使用的专用线程(Dedicated Workers)

Web Worker的大致的使用方法如下:

(在主线程里)创建一个Web Worker

const worker = new Worker('WebWorkerTest.js')
  • WebWorkerTest.js是Web Worker执行的JS代码文件
  • 加载Web Worker执行的JS代码文件需要使用HTTP或HTTPS协议,即,需要搭建网络服务器

(在主线程里)向创建的Web Worker发送数据

worker.postMessage([2, 3])

(在子线程Web Worker,即WebWorkerTest.js中)接收主线程的数据、处理并发送给主线程

onmessage = function(e) {
console.log('Message received from main script')
const workerResult = e.data[0] * e.data[1]
console.log('Posting message back to main script')
postMessage(workerResult)
}

(在主线程里)接收Web Worker发送的数据

worker.onmessage = (e) => {
console.log("Result:", e.data)
}

综上,此处创建了两个文件:WebWorkerTest.jsWebWorkerTest.html

WebWorkerTest.html代码如下:

<body>
<script>
const worker = new Worker('WebWorkerTest.js')
worker.postMessage([2, 3])
worker.onmessage = (e) => {
console.log("Result:", e.data)
}
</script>
</body>

WebWorkerTest.js代码如下:

onmessage = function(e) {
console.log('Message received from main script')
const workerResult = e.data[0] * e.data[1]
console.log('Posting message back to main script')
postMessage(workerResult)
}

运行结果如下(使用VS Code和Live Server插件):

更详细的Web Worker的使用方法,可以参考以下文档:

3. Cesium中的Web Workers

Cesium源码中,对Web Workers进行了封装,封装为TaskProcessor

源码中给出的TaskProcessor使用示例为:

const taskProcessor = new Cesium.TaskProcessor('myWorkerPath');
const promise = taskProcessor.scheduleTask({
someParameter : true,
another : 'hello'
});
if (!Cesium.defined(promise)) {
// too many active tasks - try again later
} else {
promise.then(function(result) {
// use the result of the task
});
}

查看源码,可以知道taskProcessor.scheduleTask()函数为:

TaskProcessor.prototype.scheduleTask = function (parameters, transferableObjects) {
// ...
this._worker = createWorker(this);
return Promise.resolve(canTransferArrayBuffer()).then(function (
canTransferArrayBuffer
) {
processor._worker.postMessage(
{
id: id,
parameters: parameters,
canTransferArrayBuffer: canTransferArrayBuffer,
},
transferableObjects
); return deferred.promise;
});
};

createWorker()函数为

function createWorker(processor) {
const worker = new Worker(getBootstrapperUrl());
worker.postMessage = defaultValue(
worker.webkitPostMessage,
worker.postMessage
);
// ...
return worker;
}

不难看出,Cesium中将Web Workers封装成了Promise,既有操作Promise的优雅,又有调用Web Workers带来的多线程优势

例如,在Scene\Primitive.js中,使用TaskProcessor创建Geometry

先是使用createGeometry.js的文件名创建TaskProcessor

if (!defined(createGeometryTaskProcessors)) {
createGeometryTaskProcessors = new Array(numberOfCreationWorkers);
for (i = 0; i < numberOfCreationWorkers; i++) {
createGeometryTaskProcessors[i] = new TaskProcessor("createGeometry");
}
}

然后创建promise数组:

promises.push(
createGeometryTaskProcessors[i].scheduleTask(
{
subTasks: subTasks[i],
},
subTaskTransferableObjects
)
);

最后使用Promise.all方法执行所有任务并等待结果返回:

Promise.all(promises)
.then(function (results) {
primitive._createGeometryResults = results;
primitive._state = PrimitiveState.CREATED;
})
.catch(function (error) {
setReady(primitive, frameState, PrimitiveState.FAILED, error);
});

4. 参考资料

[1] Web Workers API - Web API 接口参考 | MDN (mozilla.org)

[2] Web Worker 使用教程 - 阮一峰的网络日志 (ruanyifeng.com)

[3] Cesium原理篇:4Web Workers剖析 - fu*k - 博客园 (cnblogs.com)

[4] 浏览器的进程与线程--深入同步、异步问题 - 知乎 (zhihu.com)

[5] Web Worker 之全面讲解 - 知乎 (zhihu.com)

[6] 使用 Service Worker - Web API 接口参考 | MDN (mozilla.org)

Cesium之Web Workers的更多相关文章

  1. Web Workers

    在 Web Workers 中使用 postMessage 和 onmessage 首先,需要在客户端页面的 JavaScript 代码中 new 一个 Worker 实例出来,参数是需要在另一个线程 ...

  2. html5 Web Workers

    虽然在JavaScript中有setInterval和setTimeout函数使javaScript看起来好像使多线程执行,单实际上JavaScript使单线程的,一次只能做一件事情(关于JavaSc ...

  3. 3D拓扑自动布局之Web Workers篇

    2D拓扑的应用在电信网管和电力SCADA领域早已习以为常了,随着OpenGL特别是WebGL技术的普及,3D方式的数据可视化也慢慢从佛殿神堂步入了寻常百姓家,似乎和最近高档会所被整改为普通茶馆是一样的 ...

  4. 【HTML5】Web Workers

    什么是 Web Worker? 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成. web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性 ...

  5. HTML5学习(十)---Web Workers

    参考教程:http://www.w3school.com.cn/html5/html_5_webworkers.asp web worker 是运行在后台的 JavaScript,不会影响页面的性能. ...

  6. HTML5 Web Workers来加速您的移动Web应用

    一直以来,Web 应用程序被局限在一个单线程世界中.这的确限制了开发人员在他们的代码中的作为,因为任何太复杂的东西都存在冻结应用程序 UI 的风险.通过将多线程引入 Web 应用程… 在本文中,您将使 ...

  7. JavaScript 学习笔记 - Web Workers

    前言 本文仅是 Web Workers 的入门科普文章,不涉及太琐碎的知识点. 我们知道,在 Web Workers 出来之前,JavaScript 是单线程的.即使是 setTimeout 之类的看 ...

  8. (92)Wangdao.com_第二十五天_线程机制_H5 Web Workers 分线程任务_事件 Event

    浏览器内核 支撑浏览器运行的最核心的程序 IE 浏览器内核            Trident内核,也是俗称的IE内核Chrome 浏览器内核            统称为 Chromium 内核或 ...

  9. 通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。

    Web Workers API - Web API 接口参考 | MDNhttps://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API ...

  10. JavaScript是如何工作的:Web Workers的构建块 + 5个使用他们的场景

    摘要: 理解Web Workers. 原文:JavaScript是如何工作的:Web Workers的构建块 + 5个使用他们的场景 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这 ...

随机推荐

  1. mac 安装go语言以及配置环境变量

    Go官网下载地址:https://golang.org/dl/ Go官方镜像站(推荐):https://golang.google.cn/dl/ 其他版本自己根据系统版本下载,这里只介绍mac下载 一 ...

  2. MarkDown使用规范

    写博客的简单语法. 标题语法 一级标题:# 标题内容 二级标题:## +标题内容 三级标题:### +标题内容 后续标题语法依次增加# 注:#号后有空格 MarkDown最高支持六级标题. 字体语法 ...

  3. Flush cache via menu in D365FO

    Most of the time it is a caching issue because D365fO is (like previous versions) a master of cachin ...

  4. jenkins freestyle deploy web

    gitlab connection 选择定义好的gitlab仓库 参数化构建过程 git参数 名称 branch 描述 自定义 参数类型 分支 默认值 $branch 选项参数 名称 Status 选 ...

  5. Linux中profile、bashrc、bash_profile之间的区别和联系(转)

    /etc/profile:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行.并从/etc/profile.d目录的配置文件中搜集shell的设置. 英文描述为: # /etc/pr ...

  6. 文件的上传&预览&下载学习(四)

    0.参考博客 https://blog.csdn.net/Chengzi_comm/article/details/53037967 逻辑清晰 https://blog.csdn.net/alli09 ...

  7. Day05笔记

    01.数组类(了解) 1.目的:设计一个类,该类有数组的功能,可以存储数据,可以删除修改数据 2.设计核心数据 1.属性:指针(指向堆区空间),数组实际存储的元素个数,数组容量 2.方法:构造(开辟堆 ...

  8. T-Dubbo,最好的RPC接口测试工具,支持nacos、zookeeper两大主流注册中心,真香!

    这可能是有史以来最好用的RPC接口测试工具 文末有视频简介 获取方式 一只小Coder 简介 T-Dubbo,是一个基于Dubbo的全自动RPC接口测试平台为当下最流行的微服务架构中的RPC接口提供了 ...

  9. 用Java代码验证三门问题

    三门问题(Monty Hall problem)亦称为蒙提霍尔问题,出自美国的电视游戏节目Let's Make a Deal. 问题名字来自该节目的主持人蒙提·霍尔(Monty Hall).参赛者会看 ...

  10. Java里的对象是咋回事

    前言 在上一篇文章中,壹哥给大家介绍了Java中的类及其特点.创建过程等内容,相信你现在已经知道该如何创建一个Java类了.接下来在本篇文章中,壹哥会继续带大家学习面向对象中关于对象的内容.其实类和对 ...