JavaScript worker进程通信
在 JavaScript 中,Worker 是运行在独立线程中的,无法直接访问主线程的全局变量或 DOM。为了实现 Worker 与主线程之间的通信,以及 Worker 之间的通信,可以使用以下几种方式:
1. Worker 与主线程之间的通信
使用 postMessage 和 onmessage
-
postMessage:用于发送消息。 -
onmessage:用于接收消息。
主线程与 Worker 之间的通信流程:
- 主线程通过
worker.postMessage() 向Worker 发送消息。 -
Worker 通过self.onmessage 接收消息。 -
Worker 处理完任务后,通过self.postMessage() 将结果发送回主线程。 - 主线程通过
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');
};
解释:
-
MessageChannel:- 创建一个双向通信通道,包含两个端口(
port1 和port2)。 - 将
port1 发送给worker1,将port2 发送给worker2。
- 创建一个双向通信通道,包含两个端口(
-
port.postMessage() :- 通过端口发送消息。
-
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]);
};
解释:
-
SharedArrayBuffer:- 创建一块共享内存,允许多个
Worker 访问。
- 创建一块共享内存,允许多个
-
Atomics:- 提供原子操作,确保共享内存的读写是线程安全的。
-
Int32Array:- 将共享内存包装为整数数组,方便操作。
4. 总结
| 通信方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
postMessage |
简单消息传递 | 实现简单,支持复杂数据类型(如对象、数组) | 数据需要序列化/反序列化,性能开销较大 |
MessageChannel |
Worker 之间直接通信 |
双向通信,性能较好 | 需要手动管理端口,代码稍微复杂 |
SharedArrayBuffer |
高性能计算,共享内存 | 直接共享内存,性能极高 | 需要处理线程安全问题,代码复杂度较高 |
推荐:
- 简单任务:使用
postMessage。 -
Worker 之间通信:使用MessageChannel。 - 高性能计算:使用
SharedArrayBuffer 和Atomics。
通过这些方式,可以实现 Worker 与主线程之间,以及 Worker 之间的有效通信和数据交互。