V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zy445566
V2EX  ›  Node.js

如何更爽的在 JS 中使用多线程

  •  1
     
  •   zy445566 · 2020-09-08 09:33:05 +08:00 · 5771 次点击
    这是一个创建于 1297 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近写多线程的时候遇到一个烦恼,就是用起来实在太麻烦,不管是 WebWorker 还是 worker_threads 库,用起来都实在太麻烦了。而且很多时候 IO 密集和 CPU 密集操作很多时候是交织的,有没有一种办法,可以直接在代码中方便的使用多线程呢?

    以前我们使用 Worker 要怎么做?现在我们能怎么做?

    之前的做法:

    // ### 父进程代码
    // 比如请求网络数据,IO 操作
    const apiData = await request('/api/xxx');
    // 为了不阻塞 eventloop 开启子线程,并拿到符合要求的格式
    const goodApiData = await new Promise((resolve, reject) => {
      const worker = new Worker('子进程文件名 xxx.js', {
        workerData: apiData
      });
      worker.on('message', resolve);
      worker.on('error', reject);
    });
    // ### 子线程代码
    // 这里处理 data 数据,CPU 密集操作
    doSomething(workerData)
    // 再发送回父进程
    parentPort.postMessage(data);
    

    代码量这么大,还要写 2 个文件以上文件,数据发送过去再发送回来头都大了!!!费脑!!!

    那有没有更好的方法呢?当然使用 ncpu 就能做到!

    使用 ncpu 的做法:

    import {NCPU} from 'ncpu'
    // 比如请求网络数据,IO 操作
    const apiData = await request('/api/xxx');
    // 为了不阻塞 eventloop 开启子线程,并拿到符合要求的格式
    const goodApiData = await NCPU.run((data)=>{
      // 这里处理 data 数据,CPU 密集操作
      doSomething(data)
      return data;
    }, [apiData]) //使用数组传参,这有点类似 apply
    

    使用 ncpu 果然爽,一个回调函数就把 CPU 密集型计算搞定了。

    爽是爽,但目前有两点强制限制:

    • 回调函数不能共用上下文,因为 ncpu 是使用函数复制的方式来实现的,不会保留函数上下文,所以要求函数是强无副作用函数。
    • 传入参数都是使用 HTML structured clone algorithm方式来进行克隆的,而非原值。

    但正是这两点强制限制,使得线程更加安全了。因为但多个线程同时操作原值,会导致内存数据更新速度赶不上线程更新的速到,导致另一个线程读取数据不正确。而且我们要处理数据时,通常只需要将大循环和递归计算放入线程的回调函数中,所以这两点强制,反而不是坏事。

    目前 ncpu 的两个版本

    一个是ncpu专门为 node.js 环境设计,另一个是ncpu-web专门为浏览器环境设计。

    同时ncpu需要的最低 node.js 版本是 12,而ncpu-web浏览器要求是谷歌浏览器至少 60 以上,火狐 57 以上即可。

    在使用的时候要注意这些问题哦!

    18 条回复    2020-09-08 17:01:32 +08:00
    way2explore2
        1
    way2explore2  
       2020-09-08 09:35:59 +08:00
    我最近写了一个多进程的工具。pambdajs


    https://github.com/tim-hub/pambdajs
    zy445566
        2
    zy445566  
    OP
       2020-09-08 09:43:01 +08:00   ❤️ 1
    @way2explore2 天呐!我们想到一块去了!棒!你文档比我写的好,但我多了个前端版本,哈哈
    oubenruing
        3
    oubenruing  
       2020-09-08 09:58:11 +08:00   ❤️ 1
    微软有个 js 的多线程运行时。
    https://github.com/microsoft/napajs
    zy445566
        4
    zy445566  
    OP
       2020-09-08 10:04:03 +08:00
    @oubenruing 我知道这个,但这个缺点也明显有 C++库依赖,而且已经很多年没维护了,高版本 node 都没法使用了
    EPr2hh6LADQWqRVH
        5
    EPr2hh6LADQWqRVH  
       2020-09-08 10:19:59 +08:00 via Android
    js 的 cpu 密集型。。能密集到哪去。。。
    hhgfy
        6
    hhgfy  
       2020-09-08 10:29:15 +08:00   ❤️ 1
    不用单独拆文件倒是挺不错的
    zy445566
        7
    zy445566  
    OP
       2020-09-08 10:41:27 +08:00
    @avastms
    前端可能就是渲染和画图会用到,大部分不需要,node.js 服务端多些。其实目的就只有一个防止阻塞 eventloop 。
    之前 node.js 服务端都是用尽可能分片拆代码来实现拆解 CPU 密集运算,从而防止阻塞 eventloop,现在多了一种更方便的选择。
    LennieChoi
        8
    LennieChoi  
       2020-09-08 11:23:24 +08:00
    node12 有原生的多线程啊,好用
    zy445566
        9
    zy445566  
    OP
       2020-09-08 11:45:40 +08:00
    @LennieChoi 我就是基于原生多线程写的,就是觉得原生多线程麻烦要互相通讯
    whincwu142
        10
    whincwu142  
       2020-09-08 12:06:15 +08:00 via Android   ❤️ 1
    @way2explore2 好奇怎么做到的,看了下源码才知道,学习了👍。

    我理解的思路:将要在线程中执行的纯函数和参数序列化成字符串,通过环境产量传递给 fork 出的进程,进程中加载预定的 js 模板,解析环境变量中传入的纯函数和参数,拼接成函数通过 eval 执行,并返回结果给主进程,主进程聚合子进程结果后返回。最终实现了在主进程批量调用函数并返回结果,这些函数在子进程中执行。

    如有不对,还请指教
    OHyn
        11
    OHyn  
       2020-09-08 12:36:59 +08:00 via Android   ❤️ 1
    哇!!!
    前几天写个 webworker 搞得头痛,传来传去好麻烦。加上 webpack 打包更是好多坑,最加上 worker-lplugin 才勉强用起来。
    zy445566
        12
    zy445566  
    OP
       2020-09-08 12:47:17 +08:00
    @OHyn 对的,我最后是用了 BlobURL 实现在 webpack 打包的
    way2explore2
        13
    way2explore2  
       2020-09-08 14:41:18 +08:00
    @whincwu142 你说得完全正确。

    还没来得及仔细读你的代码?还不知道你的具体实现方式是?
    way2explore2
        14
    way2explore2  
       2020-09-08 14:44:11 +08:00
    @whincwu142 不好意思回复错了,还以为你是题主。后边的问题是问题主的 @zy445566
    way2explore2
        15
    way2explore2  
       2020-09-08 14:45:46 +08:00
    @zy445566 是啊,child_process 不在 browser 工作
    way2explore2
        16
    way2explore2  
       2020-09-08 15:22:04 +08:00
    要做一个 readme 友情链接吗?

    我把 ncpu 添加到 readme 里了 作为相关项目。

    https://github.com/tim-hub/pambdajs
    zy445566
        17
    zy445566  
    OP
       2020-09-08 16:57:28 +08:00
    @way2explore2
    其实我的实现方式有点类似。已添加友情链。


    https://github.com/zy445566/ncpu


    https://github.com/zy445566/ncpu-web
    flowfire
        18
    flowfire  
       2020-09-08 17:01:32 +08:00 via Android
    只用多线程不用 wasm 感觉没多大意义………
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3108 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 12:56 · PVG 20:56 · LAX 05:56 · JFK 08:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.