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
之间的有效通信和数据交互。
