JavaScript worker 进程通信

JavaScript worker进程通信

在 JavaScript 中,Worker​ 是运行在独立线程中的,无法直接访问主线程的全局变量或 DOM。为了实现 Worker​ 与主线程之间的通信,以及 Worker​ 之间的通信,可以使用以下几种方式:


1. Worker与主线程之间的通信

使用 postMessage​ 和 onmessage

  • postMessage​:用于发送消息。
  • onmessage​:用于接收消息。

主线程与 Worker​ 之间的通信流程:

  1. 主线程通过 worker.postMessage()​ 向 Worker​ 发送消息。
  2. Worker​ 通过 self.onmessage​ 接收消息。
  3. Worker​ 处理完任务后,通过 self.postMessage()​ 将结果发送回主线程。
  4. 主线程通过 worker.onmessage​ 接收结果。

示例:

// 主线程
const worker = new Worker('worker.js');

// 主线程发送消息给 Worker
worker.postMessage({ action: 'compute', data: [1, 2, 3] });

// 主线程接收 Worker 的返回结果
worker.onmessage = (event) => {
    console.log('Result from Worker:', event.data);
};

// worker.js
self.onmessage = (event) => {
    const { action, data } = event.data;

    if (action === 'compute') {
        // 模拟计算任务
        const result = data.reduce((sum, num) => sum + num, 0);

        // 将结果发送回主线程
        self.postMessage(result);
    }
};

2. Worker之间的通信

使用 MessageChannel

  • MessageChannel​:创建一个双向通信通道,允许两个 Worker​ 之间直接通信。

示例:

// 主线程
const worker1 = new Worker('worker1.js');
const worker2 = new Worker('worker2.js');

// 创建一个 MessageChannel
const channel = new MessageChannel();

// 将 channel.port1 发送给 worker1
worker1.postMessage({ port: channel.port1 }, [channel.port1]);

// 将 channel.port2 发送给 worker2
worker2.postMessage({ port: channel.port2 }, [channel.port2]);

// worker1.js
self.onmessage = (event) => {
    const port = event.data.port;

    // 监听来自 worker2 的消息
    port.onmessage = (event) => {
        console.log('Worker1 received:', event.data);
    };

    // 向 worker2 发送消息
    port.postMessage('Hello from Worker1');
};

// worker2.js
self.onmessage = (event) => {
    const port = event.data.port;

    // 监听来自 worker1 的消息
    port.onmessage = (event) => {
        console.log('Worker2 received:', event.data);
    };

    // 向 worker1 发送消息
    port.postMessage('Hello from Worker2');
};

解释:

  1. MessageChannel​:

    • 创建一个双向通信通道,包含两个端口(port1​ 和 port2​)。
    • port1​ 发送给 worker1​,将 port2​ 发送给 worker2​。
  2. port.postMessage() ​:

    • 通过端口发送消息。
  3. port.onmessage​:

    • 通过端口接收消息。

3. SharedArrayBuffer Atomics

适用场景:

  • 需要共享内存的场景(例如高性能计算)。
  • 允许 Worker​ 之间直接共享数据,而不需要通过消息传递。

示例:

// 主线程
const sharedBuffer = new SharedArrayBuffer(4); // 创建一个 4 字节的共享内存
const sharedArray = new Int32Array(sharedBuffer); // 将共享内存包装为 Int32Array

const worker1 = new Worker('worker1.js');
const worker2 = new Worker('worker2.js');

// 将共享内存发送给 worker1 和 worker2
worker1.postMessage(sharedArray);
worker2.postMessage(sharedArray);

// worker1.js
self.onmessage = (event) => {
    const sharedArray = event.data;

    // 修改共享内存中的数据
    Atomics.store(sharedArray, 0, 123);

    console.log('Worker1 updated shared memory:', sharedArray[0]);
};

// worker2.js
self.onmessage = (event) => {
    const sharedArray = event.data;

    // 读取共享内存中的数据
    console.log('Worker2 read shared memory:', sharedArray[0]);
};

解释:

  1. SharedArrayBuffer​:

    • 创建一块共享内存,允许多个 Worker​ 访问。
  2. Atomics​:

    • 提供原子操作,确保共享内存的读写是线程安全的。
  3. Int32Array​:

    • 将共享内存包装为整数数组,方便操作。

4. 总结

通信方式 适用场景 优点 缺点
postMessage 简单消息传递 实现简单,支持复杂数据类型(如对象、数组) 数据需要序列化/反序列化,性能开销较大
MessageChannel Worker​ 之间直接通信 双向通信,性能较好 需要手动管理端口,代码稍微复杂
SharedArrayBuffer 高性能计算,共享内存 直接共享内存,性能极高 需要处理线程安全问题,代码复杂度较高

推荐:

  • 简单任务:使用 postMessage​。
  • Worker之间通信:使用 MessageChannel​。
  • 高性能计算:使用 SharedArrayBuffer​ 和 Atomics​。

通过这些方式,可以实现 Worker​ 与主线程之间,以及 Worker​ 之间的有效通信和数据交互。

image.png

留下你的脚步
推荐阅读