图示:浏览器、主线程、工作者线程之间的关系和通信方式(附:ArrayBuffer 详解)

浏览器、主线程、工作者线程之间的关系和通信方式

一、基本概念

组件 说明
浏览器 宿主环境,提供运行网页的容器,包含多个进程(如渲染进程、网络进程、GPU 进程等)
主线程(Main Thread) 属于渲染进程,负责解析 HTML/CSS、执行 JavaScript、处理 DOM、响应用户交互等
工作者线程(Worker Thread) 由主线程创建,用于在后台执行 JavaScript(如 Web Worker),不能访问 DOM

二、通信机制:消息传递(Message Passing)

主线程与工作者线程之间 不能共享内存 ,只能通过 postMessage() + onmessage 异步传递消息(结构化克隆算法序列化数据)。


三、文本流程图


四、通信流程步骤(以 Web Worker 为例)

  1. 主线程 创建 Worker:

    Js

    javascript 复制代码
    const worker = new Worker('worker.js');
  2. 主线程 发送消息:

    Js

    javascript 复制代码
    worker.postMessage({ data: 'Hello Worker!' });
  3. 工作者线程worker.js)接收并处理:

    Js

    javascript 复制代码
    self.onmessage = (event) => {
    //heavyComputation() 并不是一个 JavaScript 内置函数,而是一个示意性的占位函数名,用来代表"耗时的计算任务"。
    //模拟在 Worker 线程中执行的、不适合放在主线程中的 CPU 密集型操作。
      const result = heavyComputation(event.data);
      self.postMessage(result); // 发回结果
    };
  4. 主线程 接收回传结果:

    Js

    javascript 复制代码
    worker.onmessage = (event) => {
      console.log('Result:', event.data);
    };
  5. 注意 :所有通信都是 异步、非阻塞 的,且数据被 复制(非引用)。


常见的 "Heavy Computation" 场景

类型 示例
数学计算 大数运算、矩阵乘法、加密/解密
数据处理 解析大型 JSON、CSV 转换、图像像素处理
算法执行 排序大量数据、路径搜索(如 A*)、模拟(物理/金融)
游戏逻辑 AI 决策、地图生成

为什么要在 Worker 中做这些?

因为 JavaScript 是单线程的(在主线程中)。如果在主线程执行耗时任务:

  • 页面会卡顿或冻结
  • 用户无法点击、滚动
  • 动画掉帧(jank)

而 Web Worker 提供了真正的多线程能力 (尽管通信受限),让这些任务在后台运行,不阻塞 UI

五、限制与注意事项

  • 可以传递:基本类型、对象(通过结构化克隆)、ArrayBuffer(可转移所有权)
  • 不能传递 :DOM 节点、函数、Error 对象、某些内置对象(如 window
  • 🔒 线程隔离 :Worker 无法访问 windowdocumentlocalStorage 等主线程全局对象
  • 🧩 多 Worker:一个页面可创建多个 Worker,彼此独立

六、扩展:其他类型的 Worker

类型 特点
Dedicated Worker 默认类型,仅创建它的主线程可通信
Shared Worker 多个 tab/iframe 可共享同一个 Worker
Service Worker 用于拦截网络请求、实现离线缓存(运行在独立线程,但生命周期由浏览器控制)

核心要点总结

  1. 线程隔离

    • 主线程:UI渲染、DOM操作、用户交互

    • Worker线程:纯计算、无DOM访问、独立全局作用域

  2. 通信方式

    • postMessage():结构化克隆,安全但有一定开销

    • MessageChannel:高效双向通信

    • SharedArrayBuffer:共享内存,零拷贝但需要同步

  3. 生命周期

    复制代码
    创建 → 通信 → 处理 → 响应 → [终止/重用]
  4. 适用场景

    • ✅ 图像/视频处理

    • ✅ 大数据计算

    • ✅ 复杂算法

    • ❌ DOM操作

    • ❌ 同步API调用

  5. Vue 3 集成

    • 使用 Composition API 封装 Worker 逻辑

    • 通过响应式数据同步状态

    • 注意组件卸载时的资源清理


图示

浏览器多线程架构示意图


通信流程图


线程与内存关系图


消息传递对比图


实际代码示例流程图


关键特点总结表



ArrayBuffer 是 JavaScript 中用于表示通用、固定长度的二进制数据缓冲区 的对象。它本身不能直接读写数据,而是作为底层内存的"容器",需要通过 视图(如 Uint8ArrayFloat32Array 等) 来访问其内容。

主线程与 Worker 线程通信 的上下文中,ArrayBuffer 有一个非常重要的特性:可以"转移"(transfer)所有权 ,而不是复制,从而实现零拷贝、高性能的数据传递


ArrayBuffer 详解

ArrayBuffer 是 JavaScript 中用于表示通用、固定长度的二进制数据缓冲区的对象。它本身不能直接读写数据,而是作为底层内存的"容器",需要通过 视图(如 Uint8ArrayFloat32Array 等) 来访问其内容。

主线程与 Worker 线程通信 的上下文中,ArrayBuffer 有一个非常重要的特性:可以"转移"(transfer)所有权 ,而不是复制,从而实现零拷贝、高性能的数据传递


🔍 1. 基本概念

javascript 复制代码
// 创建一个 1024 字节的 ArrayBuffer
const buffer = new ArrayBuffer(1024);

// 创建视图来读写数据
const uint8View = new Uint8Array(buffer);
uint8View[0] = 255; // 写入第一个字节
  • ArrayBuffer:原始二进制内存块(类似 C 的 malloc 分配的内存)
  • TypedArray(如 Uint8Array, Int32Array, Float64Array):提供类型化访问方式
  • DataView:提供更灵活的、可指定字节序的读写能力

🔄 2. 在主线程与 Worker 之间传递 ArrayBuffer

✅ 默认行为:结构化克隆(复制)

javascript 复制代码
// 主线程
const buffer = new ArrayBuffer(1024);
worker.postMessage(buffer); // ← 默认会复制整个 buffer
console.log(buffer.byteLength); // 仍然是 1024(因为是复制)

这种方式适合小数据,但大数据(如图像、音频)会因复制而性能低下。


⚡ 推荐方式:转移所有权(Transferable)

javascript 复制代码
// 主线程
const buffer = new ArrayBuffer(1024 * 1024); // 1MB
worker.postMessage(buffer, [buffer]); // ← 第二个参数:transferList
console.log(buffer.byteLength); // 输出 0!因为所有权已转移
javascript 复制代码
// worker.js
self.onmessage = (e) => {
  const receivedBuffer = e.data;
  console.log(receivedBuffer.byteLength); // 1048576(1MB)
  // 可以安全使用,无需复制
};

优势

  • 零内存拷贝

  • 极高效率(适用于音视频处理、WebGL、WASM 等场景)

  • 避免主线程卡顿
    注意

  • 转移后,原线程中的 ArrayBuffer 变为不可用byteLength === 0

  • 只能转移 ArrayBuffer 本身,不能转移 TypedArray(但可以转移其 .buffer


🧩 3. 实际应用场景

场景:图像像素处理

javascript 复制代码
// 主线程:从 canvas 获取像素数据
const imageData = ctx.getImageData(0, 0, width, height);
const buffer = imageData.data.buffer; // Uint8ClampedArray 的 buffer

// 转移到 Worker 处理(如滤镜)
worker.postMessage({ buffer, width, height }, [buffer]);

// 注意:imageData.data 现在已失效!
javascript 复制代码
// worker.js:处理像素
self.onmessage = (e) => {
  const { buffer, width, height } = e.data;
  const pixels = new Uint8ClampedArray(buffer);
  
  // 应用灰度滤镜
  for (let i = 0; i < pixels.length; i += 4) {
    const avg = (pixels[i] + pixels[i+1] + pixels[i+2]) / 3;
    pixels[i] = pixels[i+1] = pixels[i+2] = avg;
  }
  
  self.postMessage(buffer, [buffer]); // 处理完再转回主线程
};
javascript 复制代码
// 主线程接收处理后的图像
worker.onmessage = (e) => {
  const processedBuffer = e.data;
  const processedPixels = new Uint8ClampedArray(processedBuffer);
  const newImageData = new ImageData(processedPixels, width, height);
  ctx.putImageData(newImageData, 0, 0);
};

📌 总结

特性 说明
是什么 二进制数据的原始缓冲区
如何访问 通过 TypedArrayDataView
通信方式 可复制(默认)或转移(高效)
转移语法 postMessage(data, [arrayBuffer])
适用场景 音频/视频处理、图像操作、WASM 数据交换、大文件解析等

如果你正在处理大量二进制数据(比如从 fetch 获取的 arrayBuffer()FileReader 读取的文件、或 WebGL 纹理),务必考虑使用 transfer 模式,这是 Web Worker 高性能通信的关键技巧之一。

相关推荐
Wect1 天前
浏览器缓存机制
前端·面试·浏览器
FliPPeDround5 天前
浏览器扩展 E2E 测试的救星:vitest-environment-web-ext 让你告别繁琐配置
e2e·浏览器·测试
SuperEugene5 天前
浏览器存储:localStorage / sessionStorage / cookie 应该怎么用
前端·javascript·面试·浏览器
宁雨桥5 天前
浏览器渲染原理
前端·浏览器·原理
YZ0997 天前
2026年如何批量保存小红书作者主页的视频、图片和文案?
经验分享·浏览器·插件
程序员ys7 天前
网页白屏的原理与优化
前端·性能优化·浏览器
Wect9 天前
从输入URL到页面显示的完整技术流程
前端·面试·浏览器
NEXT069 天前
从输入 URL 到页面展示的完整链路解析
网络协议·面试·浏览器
CappuccinoRose12 天前
CSS 语法学习文档(十五)
前端·学习·重构·渲染·浏览器
REDcker13 天前
Media Source Extensions (MSE) 详解
前端·网络·chrome·浏览器·web·js