商丘市网站建设_网站建设公司_色彩搭配_seo优化
2025/12/18 1:02:34 网站建设 项目流程

由于Spectre和Meltdown的漏洞,所有主流浏览器在2018年1月就禁用了sharedArrayBuffer。

从2019年开始,有些浏览器开始逐步重新启用这一特性。


既不克隆,也不转移,sharedArrayBuffer作为ArrayBuffer能够在不同浏览器上下文间共享。


在把sharedArrayBuffer传给postMessage()时,浏览器只会传递原始缓冲区的引用。

结果是,两个不同的JavaScript上下文会分别维护对同一个内存块的引用。每个上下文都可以随意修改这个缓冲区,就跟修改常规ArrayBuffer一样。


多个上下文访问SharedArrayBuffer时,如果同时对缓冲区执行操作,就可能出现资源争用问题。


Atomics API通过强制同一时刻只能对缓冲区执行一个操作,可以让多个上下文安全地读写一个SharedArrayBuffer。


SharedArrayBuffer

SharedArrayBuffer和ArrayBuffer具有同样的API。


在多个执行上下文间共享内存意味着并发线程操作成为可能。


传统JavaScript操作对于并发内存访问导致的资源争用没有提供保护。


Atomics API解决了这个问题,可以保证SharedArrayBuffer上的JavaScript操作是线程安全的。

原子操作基础

任何全局上下文中都有一个Atomics 对象,对象上暴露了用于执行线程安全操作的一套静态方法。


SharedArrayBuffer 和 Atomics API 详解


一、SharedArrayBuffer(共享内存)

1. 基本概念

  • 共享内存:允许多个线程(Web Workers)共享同一块内存区域

  • 主要用途:在多线程环境中高效地共享和操作数据

  • 特点:在 Web Workers 之间传递时不复制数据,而是共享引用


2. 基本使用

javascript

// 主线程 const sharedBuffer = new SharedArrayBuffer(1024); // 创建1KB共享内存 const int32View = new Int32Array(sharedBuffer); // 创建视图 // 传递给 Worker const worker = new Worker('worker.js'); worker.postMessage(sharedBuffer); // Worker 中接收 // self.onmessage = function(e) { // const sharedBuffer = e.data; // const int32View = new Int32Array(sharedBuffer); // }

3. 安全要求

  • 必须设置 COOP/COEP 头部:

http

Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp

二、Atomics API

1. 为什么需要 Atomics?

  • 解决多线程并发访问时的竞争条件问题

  • 确保操作的原子性(atomic operations)

  • 提供内存同步机制


2. 主要方法分类

原子操作(读写和运算):

javascript

const sharedBuffer = new SharedArrayBuffer(16); const intArray = new Int32Array(sharedBuffer); // 基础原子操作 Atomics.store(intArray, 0, 42); // 原子写入 let value = Atomics.load(intArray, 0); // 原子读取 // 原子运算 Atomics.add(intArray, 0, 5); // 原子加法(返回旧值) Atomics.sub(intArray, 0, 3); // 原子减法 Atomics.and(intArray, 0, 0b1111); // 原子与运算 Atomics.or(intArray, 0, 0b1100); // 原子或运算 Atomics.xor(intArray, 0, 0b1010); // 原子异或运算 // 原子交换 let old = Atomics.exchange(intArray, 0, 100); // 交换为新值 let result = Atomics.compareExchange( intArray, 0, expectedValue, newValue ); // 仅在当前值等于期望值时交换

同步和等待:

javascript

// 等待和通知机制(类似锁) Atomics.wait(intArray, index, expectedValue, timeout); Atomics.notify(intArray, index, count); // 唤醒等待的线程 // 使用示例 // Worker 1: Atomics.store(intArray, 0, 0); Atomics.wait(intArray, 0, 0); // 等待值变为非0 // Worker 2: Atomics.store(intArray, 0, 1); Atomics.notify(intArray, 0, 1); // 唤醒一个等待的Worker

3. 典型用例示例

生产者-消费者模式:

javascript

// 创建共享内存 const sharedBuffer = new SharedArrayBuffer(12); const sharedArray = new Int32Array(sharedBuffer); // 主线程(生产者) const worker = new Worker('consumer.js'); worker.postMessage(sharedBuffer); // 生产者逻辑 function produce(data) { // 等待缓冲区有空位 while (Atomics.load(sharedArray, 0) !== 0) { Atomics.wait(sharedArray, 0, 1); } // 写入数据 Atomics.store(sharedArray, 2, data); // 设置标志通知消费者 Atomics.store(sharedArray, 0, 1); Atomics.notify(sharedArray, 0, 1); } // Worker(消费者) self.onmessage = function(e) { const sharedArray = new Int32Array(e.data); while (true) { // 等待有数据可读 if (Atomics.load(sharedArray, 0) === 0) { Atomics.wait(sharedArray, 0, 0); continue; } // 读取数据 const data = Atomics.load(sharedArray, 2); // 处理数据 console.log('Consumed:', data); // 重置标志通知生产者 Atomics.store(sharedArray, 0, 0); Atomics.notify(sharedArray, 0, 1); } };

互斥锁实现:

javascript

class Mutex { constructor(sharedArray, index = 0) { this.lock = new Int32Array(sharedArray); this.index = index; } acquire() { while (true) { // 尝试获取锁(0表示未锁定) if (Atomics.compareExchange(this.lock, this.index, 0, 1) === 0) { return; } // 等待锁释放 Atomics.wait(this.lock, this.index, 1); } } release() { // 释放锁 Atomics.store(this.lock, this.index, 0); Atomics.notify(this.lock, this.index, 1); } execute(callback) { this.acquire(); try { return callback(); } finally { this.release(); } } }

三、最佳实践和注意事项

1.性能考虑

  • 尽量减少共享内存的访问

  • 使用适当大小的内存块

  • 避免频繁的跨线程通信

2. 错误处理

javascript

try { // 检查浏览器支持 if (typeof SharedArrayBuffer !== 'undefined') { const buffer = new SharedArrayBuffer(1024); } } catch (error) { console.error('SharedArrayBuffer not supported:', error); }

3. 调试技巧

  • 使用断点和内存查看器

  • 添加详细的日志记录

  • 实现超时机制防止死锁


四、浏览器支持和安全限制

支持情况:

  • Chrome 68+(需要安全上下文)

  • Firefox 79+

  • Safari 15.4+

  • Edge 79+


安全限制:

  1. 必须使用 HTTPS

  2. 需要正确的 HTTP 头部

  3. 某些 API 可能被限制使用


总结

SharedArrayBuffer 和 Atomics API 为 JavaScript 带来了真正的多线程编程能力,但同时也增加了复杂性。使用时需要注意:

  • 线程安全:始终使用原子操作访问共享内存

  • 性能:避免不必要的共享访问

  • 调试:多线程调试较为困难,需有良好设计

  • 兼容性:检查浏览器支持并准备降级方案


这些 API 特别适用于:

  • 高性能计算

  • 实时数据处理

  • 游戏和图形应用

  • 大规模并行计算任务


Atomics 对象方法总结表

方法类别方法名语法描述返回值
原子操作store()Atomics.store(typedArray, index, value)原子方式将给定值存储在数组的指定位置设置的值
load()Atomics.load(typedArray, index)原子方式从数组的指定位置读取值读取的值
exchange()Atomics.exchange(typedArray, index, value)原子方式将指定位置的值替换为新值替换前的旧值
compareExchange()Atomics.compareExchange(typedArray, index, expectedValue, replacementValue)仅在当前值等于期望值时原子替换为新值替换前的旧值(无论是否替换)
原子运算add()Atomics.add(typedArray, index, value)原子加法:将指定值加到当前位置的值上运算前的旧值
sub()Atomics.sub(typedArray, index, value)原子减法:从当前位置的值减去指定值运算前的旧值
and()Atomics.and(typedArray, index, value)原子按位与:与指定值进行按位与运算运算前的旧值
or()Atomics.or(typedArray, index, value)原子按位或:与指定值进行按位或运算运算前的旧值
xor()Atomics.xor(typedArray, index, value)原子按位异或:与指定值进行按位异或运算运算前的旧值
同步等待wait()Atomics.wait(typedArray, index, expectedValue, timeout)使线程等待,直到指定位置的值发生变化或超时"ok"(正常唤醒),"timed-out"(超时),"not-equal"(值不匹配)
notify()Atomics.notify(typedArray, index, count)唤醒正在等待指定位置的线程成功唤醒的线程数量
实用方法isLockFree(size)Atomics.isLockFree(size)检查指定大小的操作是否在硬件层面是无锁的true(无锁)或false(需要锁)

参数说明表

参数类型描述
typedArrayInt8Array
Uint8Array
Int16Array
Uint16Array
Int32Array
Uint32Array
BigInt64Array
BigUint64Array
共享的TypedArray对象,基于SharedArrayBuffer创建
indexnumber在typedArray中操作的索引位置
valuenumberBigInt要存储、添加或操作的值(类型需与TypedArray匹配)
expectedValuenumberBigInt期望的当前值(用于compareExchange和wait)
replacementValuenumberBigInt替换的新值
timeoutnumber最大等待时间(毫秒),可选,默认无限等待
countnumber要唤醒的等待线程数量,可选,默认唤醒所有
sizenumber字节大小(通常为1、2、4、8、16)

使用场景速查表

场景推荐方法示例用途
基本读写store()/load()简单的线程安全读写操作
计数器add()/sub()多线程共享计数器
状态标志compareExchange()实现锁、信号量等同步原语
线程等待wait()/notify()生产者-消费者模式、条件等待
位操作and()/or()/xor()位标志、状态机控制
数据交换exchange()无锁队列、缓冲区交换

注意事项表

项目说明
类型一致性TypedArray的类型必须与操作的值类型匹配
索引边界索引必须在TypedArray的有效范围内
超时处理wait()的timeout参数控制最大等待时间
线程唤醒notify()可指定唤醒线程数量,避免"惊群效应"
性能优化isLockFree()可帮助选择最优的数据大小
浏览器支持部分浏览器可能对某些TypedArray类型支持有限

典型示例代码片段

javascript

// 创建共享内存 const sharedBuffer = new SharedArrayBuffer(1024); const int32Array = new Int32Array(sharedBuffer); // 原子写入和读取 Atomics.store(int32Array, 0, 42); let value = Atomics.load(int32Array, 0); // 42 // 原子运算 let oldValue = Atomics.add(int32Array, 0, 10); // 返回42,位置0的值变为52 // 比较并交换 let result = Atomics.compareExchange(int32Array, 0, 52, 100); // 成功返回52 // 等待和通知 // 线程1: Atomics.wait(int32Array, 0, 100); // 等待值改变 // 线程2: Atomics.store(int32Array, 0, 200); Atomics.notify(int32Array, 0, 1); // 唤醒一个等待线程

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询