码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Cesium之Web Workers


    1. 引言

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

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

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

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

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

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

    • 浏览器的进程与线程--深入同步、异步问题 - 知乎 (zhihu.com)
    • Web Worker 之全面讲解 - 知乎 (zhihu.com)

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

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

    • Web Workers API - Web API 接口参考 | MDN (mozilla.org)
    • Web Worker 使用教程 - 阮一峰的网络日志 (ruanyifeng.com)

    本文描述浏览器中的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.js和WebWorkerTest.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插件):

    image-20230425142342609

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

    • Web Workers API - Web API 接口参考 | MDN (mozilla.org)
    • Web Worker 使用教程 - 阮一峰的网络日志 (ruanyifeng.com)

    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)

  • 相关阅读:
    C++智能指针之weak_ptr
    凯美瑞 vs 太空船:Web3 游戏生长的两条路径
    win11启动docker报错
    C# WebService 发布到本地IIS----局域网访问/外网访问
    Redis作为缓存,mysql的数据如何与redis进行同步?
    《算法通关村——反转字符串中的单词问题解析》
    分布式事务和分布式锁
    Android一个有用又有趣的知识点:BindingAdapter
    img 响应式图片的实现(含srcset属性、sizes属性的使用方法,设备像素比详解)
    【机器学习】支持向量回归
  • 原文地址:https://www.cnblogs.com/jiujiubashiyi/p/17352716.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号