Web Worker多线程模型与浏览器并行计算
JavaScript单线程执行模型在计算密集型任务面前存在天然瓶颈。Web Worker为浏览器提供了真正的操作系统级线程,Worker线程运行在独立的全局上下文中,无法直接访问DOM,但可以执行任意JavaScript计算。主线程与Worker之间通过postMessage通信,数据默认采用结构化克隆算法复制传输。
Web Worker分为Dedicated Worker和Shared Worker两类。Dedicated Worker一对一绑定创建者,Shared Worker可被多个页面共享。计算密集型场景推荐Dedicated Worker,跨页面通信推荐Shared Worker。
Dedicated Worker基本通信与数据传输
主线程创建Worker并建立双向通信:
// main.js - 主线程
const worker = new Worker('compute-worker.js');
// 发送任务到Worker
worker.postMessage({
type: 'START_COMPUTE',
data: largeArray,
config: { iterations: 1000, threshold: 0.001 }
});
// 接收Worker返回结果
worker.onmessage = (event) => {
const { type, result, progress } = event.data;
if (type === 'PROGRESS') {
updateProgressBar(progress);
} else if (type === 'COMPLETE') {
renderResult(result);
}
};
// compute-worker.js - Worker线程
self.onmessage = (event) => {
const { type, data, config } = event.data;
if (type === 'START_COMPUTE') {
const result = heavyComputation(data, config);
self.postMessage({ type: 'COMPLETE', result });
}
};
function heavyComputation(data, config) {
let result = [];
for (let i = 0; i < config.iterations; i++) {
const partial = processChunk(data, i, config.threshold);
result.push(partial);
if (i % (config.iterations / 10) === 0) {
self.postMessage({ type: 'PROGRESS', progress: i / config.iterations });
}
}
return result;
}
postMessage默认使用结构化克隆复制数据,对于大型ArrayBuffer会产生显著的拷贝开销。10MB数据传输可能消耗数十毫秒,在高频通信场景下会成为瓶颈。
Transferable Objects零拷贝传输机制
Transferable Objects机制允许将ArrayBuffer的所有权从主线程转移给Worker,实现零拷贝传输。转移后原线程对该缓冲区的引用将失效:
// 主线程 - 零拷贝传输
const buffer = new Float64Array(1000000); // 约8MB数据
// 填充数据...
// 通过transferList转移buffer所有权
worker.postMessage({ type: 'PROCESS', buffer: buffer.buffer }, [buffer.buffer]);
// 转移后,buffer在主线程变为0长度
console.log(buffer.length); // 0 - 所有权已转移
// Worker端接收
self.onmessage = (event) => {
const { buffer } = event.data;
const floatArray = new Float64Array(buffer);
// 直接操作,无需拷贝
const result = computeInPlace(floatArray);
// 将结果转移回主线程
self.postMessage({ type: 'RESULT', buffer: result.buffer }, [result.buffer]);
};
Transferable Objects支持的类型包括:ArrayBuffer、MessagePort、ImageBitmap、OffscreenCanvas。传输后原线程引用被剥离,避免双写导致的内存占用倍增和竞态条件。
SharedArrayBuffer多线程共享内存编程
SharedArrayBuffer允许多个Worker线程共享同一块内存区域,无需数据拷贝即可实现线程间数据交换。SharedArrayBuffer需要严格的安全上下文(Cross-Origin-Isolation):
// 服务端必须配置COOP/COEP响应头
// Cross-Origin-Opener-Policy: same-origin
// Cross-Origin-Embedder-Policy: require-corp
// 主线程创建共享缓冲区
const sharedBuffer = new SharedArrayBuffer(4 * 1024 * 1024); // 4MB共享内存
const sharedArray = new Float64Array(sharedBuffer);
// 初始化数据
for (let i = 0; i < sharedArray.length; i++) {
sharedArray[i] = Math.random();
}
// 创建多个Worker共享同一内存
const workerCount = navigator.hardwareConcurrency || 4;
const workers = [];
const chunkSize = sharedArray.length / workerCount;
for (let i = 0; i < workerCount; i++) {
const worker = new Worker('shared-worker.js');
worker.postMessage({
sharedBuffer,
offset: i * chunkSize,
length: chunkSize,
workerId: i
});
workers.push(worker);
}
Worker端使用Atomics操作保证共享内存的原子访问:
// shared-worker.js
self.onmessage = (event) => {
const { sharedBuffer, offset, length, workerId } = event.data;
const sharedArray = new Float64Array(sharedBuffer);
// 并行计算各自负责的分片
for (let i = offset; i < offset + length; i++) {
sharedArray[i] = Math.sqrt(sharedArray[i]) * 2.5;
}
// 使用Atomics同步完成信号
const syncBuffer = new Int32Array(sharedBuffer);
const finishedIndex = syncArray.length - 1;
const prev = Atomics.add(syncArray, finishedIndex, 1);
if (prev + 1 === workerCount) {
self.postMessage({ type: 'ALL_COMPLETE' });
}
};
Atomics原子操作与同步原语
SharedArrayBuffer的并发访问必须通过Atomics对象进行同步。Atomics提供了load、store、add、sub、and、or、xor、compareExchange、exchange等原子操作,以及wait/notify实现类似条件变量的同步机制:
// 使用Atomics实现简单的自旋锁
class SpinLock {
constructor(sharedBuffer, byteOffset) {
this.lock = new Int32Array(sharedBuffer, byteOffset, 1);
}
acquire() {
while (Atomics.compareExchange(this.lock, 0, 0, 1) !== 0) {
// 自旋等待,可加入短暂yield降低CPU占用
}
}
release() {
Atomics.store(this.lock, 0, 0);
}
}
// 使用Atomics.wait/notify实现生产者-消费者
Atomics.store(dataArray, writeIndex, value);
Atomics.notify(dataArray, writeIndex);
Atomics.wait(dataArray, readIndex, expectedValue, timeout);
Web Worker与SharedArrayBuffer的组合在图像处理、大规模数值计算、音视频编解码等场景中可带来2-8倍的性能提升,具体取决于CPU核心数和计算密集程度。需要特别注意浏览器兼容性,SharedArrayBuffer目前仅在安全上下文下可用,部分企业网络代理可能会剥离COOP/COEP响应头导致功能降级。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/webworker-duo-xian-cheng-ji-suan-yu-sharedarraybuffer-gong/