• nodejs 工作线程的基本使用(worker_threads)


    前言

    nodejs从第十版开始,支持了真正的多线程编程,今天我们就来学习一下worker_threads工作线程的一些基本使用方法。

    主线程与工作线程

    我们做一个特别简单的例子,主线程给工作线程提供参数,工作线程负责简单的加法计算(实际场景可以是很复杂的计算),计算完毕返回给主线程。

    运行主线程即可得到执行结果。

    主线程(main.js

    1. 引入工作线程构造函数Worker传入work.js的文件地址创建工作线程work
    2. 工作线程work可以通过postMessage传递数据,这里传递了一个对象过去。
    3. 工作线程work可以通过监听message方法获得工作线程传来的数据。
    import {Worker} from 'worker_threads'
    
    const work = new Worker('./work.js') 
    
    work.postMessage({x: 1, y: 2})
    
    work.on('message', value => {
        console.log(value)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    工作线程(work.js

    1. 引入parentPort可以与主线程(引入了该工作线程的线程)进行交互。
    2. parentPort.onmessage方法可以获取主线程传来的数据,与work.postMessage相对应,传来的数据在event.data中,event还有一些其他参数。
    3. parentPort.postMessage方法可以传递数据去主线程,与work的监听message方法相对应
    import {parentPort} from 'worker_threads'
    
    const getSum = (x, y) => {
        return x + y  
    }
    
    parentPort.onmessage = (event) => {
        const {x, y} = event.data
        const res = getSum(x, y)
        parentPort.postMessage(res)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    工作线程交互

    假如我们需要两个工作线程之间进行交互,需要依赖主线程搭建桥梁。

    至于搭建桥梁的工具,则是依赖中的MessageChannel,它可以提供两个可以互相传递数据的端口。

    下面的例子简单演示了端口1向端口2传递数据,一个传递数据一个接收数据。

    反之也一样的,端口2向端口1传递数据也是同样的方法。

    import {MessageChannel} from 'worker_threads'
    
    const {port1, port2} = new MessageChannel()
    
    port1.postMessage('123')
    
    port2.onmessage = ev => {
        console.log(ev.data) 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    那我们怎么利用MessageChannel来进行工作线程间的交互呢?

    只要分别将两个端口传去对应的工作线程不就好了吗?

    1. 传递端口不可以简单的使用postMessage的第一个数据入参传递,只能通过第二个参数并且以数组的形式传递。
    2. 我们在传送对象数据中加入参数typetypeport用告诉工作线程我们传递的是端口,需要进行端口的监听操作
    3. 同时我们传递typesend的数据,负责端口的发送数据操作
    4. 两边既有监听又有发送,即实现了通信。

    主线程(main.js

    import {Worker, MessageChannel} from 'worker_threads'
    
    let work1 = new Worker('./work1.js')
    let work2 = new Worker('./work2.js')
    
    const {port1, port2} = new MessageChannel()
    
    work1.postMessage({type: 'port'}, [port1])
    work2.postMessage({type: 'port'}, [port2])
    
    work1.postMessage({type: 'send', value: '从线程1向线程2传递信息'})
    work2.postMessage({type: 'send', value: '从线程2向线程1传递信息'})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    工作线程1(work1.js

    1. 从主线程传来的port可以通过event.ports[0]得到,我们在工作线程1创建好port1的监听事件,随时可以接收到port2传来的数据。
    2. 同时我们在额外创建一个send类型事件,就可以在主线程,控制两个工作线程之间传递消息了。
    import {parentPort} from 'worker_threads'
    
    let port1
    
    parentPort.onmessage = (event) => {
        const {type, value} = event.data
        switch (type) {
            case 'port':
                port1 = event.ports[0]
                port1.onmessage = ev => {
                    console.log(ev.data) 
                }
                break
            case 'send':
                port1.postMessage(value)
                break
            default:
                break
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    工作线程2(work2.js

    与工作线程1同理。

    import {parentPort} from "worker_threads";
    
    let port2
    
    parentPort.onmessage = (event) => {
        const {type, value} = event.data
        switch (type) {
            case 'port':
                port2 = event.ports[0]
                port2.onmessage = ev => {
                    console.log(ev.data)
                }
                break
            case 'send':
                port2.postMessage(value)
                break
            default:
                break
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    尾言

    内容并不多,主要是nodejs中需要用到工作线程的实际可用场景并不多,将来若有机会还会继续补充。

    如果有任何错误或者建议,欢迎指出,我会及时修改。

    如果文章对你有帮助的话,欢迎点赞收藏~

  • 相关阅读:
    MySQL删除数据库(DROP DATABASE语句)
    Python(一)——简介与Windows平台安装
    python代码中经常看到,if __name__ == “__main__“,作用是啥
    POSTGIS数据库操作
    LeetCode 101. 对称二叉树
    FFmpeg AAC文件和H264文件合成MP4/FLV文件
    JsonPath:针对json的强大的规则解析与参数查找工具
    js - 原生的一些滚动属性和方法(scroll)
    go 语言的映射(map)
    Spring-AOP
  • 原文地址:https://blog.csdn.net/weixin_43877799/article/details/128096650