0

0

JS如何实现SharedArrayBuffer?共享内存

煙雲

煙雲

发布时间:2025-08-19 08:03:01

|

305人浏览过

|

来源于php中文网

原创

JavaScript通过SharedArrayBuffer实现共享内存,允许多个线程访问同一内存块,提升大数据处理性能。2. 创建SharedArrayBuffer实例并用postMessage传递引用,实现主线程与Worker间高效通信。3. 必须配合Atomics对象进行原子操作,防止数据竞争。4. 使用受限于跨域隔离策略,需服务器配置COOP和COEP头部以确保安全。5. 相比postMessage的序列化复制,SharedArrayBuffer避免了数据传输开销,适合高性能场景。

js如何实现sharedarraybuffer?共享内存

在JavaScript中实现共享内存,核心就是利用

SharedArrayBuffer
。它提供了一种机制,让多个执行上下文(比如主线程和Web Worker)能够访问同一个内存块,而不是通过序列化和反序列化来复制数据,从而在处理大量数据时显著提升性能。

解决方案

要使用

SharedArrayBuffer
,你首先需要创建一个它的实例,这个实例代表了一块可以在不同线程间共享的固定大小的原始二进制数据缓冲区。与普通的
ArrayBuffer
不同,
SharedArrayBuffer
的实例在创建后可以被多个Worker或主线程引用,并且它们操作的是同一份内存。

创建一个

SharedArrayBuffer
很简单:

const sharedBuffer = new SharedArrayBuffer(1024); // 创建一个1KB的共享内存
const sharedInt32Array = new Int32Array(sharedBuffer); // 创建一个视图,以便操作其中的整数数据

创建后,你可以通过

postMessage
方法将这个
sharedBuffer
传递给Web Worker。但这里有个关键点,传递的不是数据的副本,而是对同一个
SharedArrayBuffer
对象的引用。

在主线程:

const worker = new Worker('worker.js');
const sharedBuffer = new SharedArrayBuffer(1024);
const sharedArray = new Int32Array(sharedBuffer);

// 初始化一些数据
for (let i = 0; i < sharedArray.length; i++) {
    sharedArray[i] = i;
}

worker.postMessage({ buffer: sharedBuffer });

worker.onmessage = (event) => {
    console.log('Worker更新后的数据:', sharedArray[0]); // 看看worker是不是真的改了
};

worker.js
中:

onmessage = (event) => {
    const sharedBuffer = event.data.buffer;
    const sharedArray = new Int32Array(sharedBuffer);

    // Worker修改共享内存中的数据
    sharedArray[0] = 999;
    console.log('Worker内部修改数据:', sharedArray[0]);

    // 通知主线程,或者继续操作
    postMessage('数据已更新');
};

需要特别注意的是,为了防止数据竞争(race condition)和确保操作的原子性,当多个线程同时读写共享内存时,必须配合

Atomics
对象进行同步操作。
Atomics
提供了一系列原子操作,比如
Atomics.add()
Atomics.load()
Atomics.store()
Atomics.wait()
Atomics.notify()
,它们保证了对共享内存的读写操作是不可中断的,从而避免了数据损坏或不一致。

一个简单的原子操作示例:

// 在主线程或Worker中
Atomics.add(sharedArray, 0, 1); // 原子地将sharedArray[0]的值增加1
const value = Atomics.load(sharedArray, 0); // 原子地读取sharedArray[0]的值

另外,

SharedArrayBuffer
的使用受到了严格的浏览器安全限制。它要求页面必须启用跨域隔离(Cross-Origin Isolation),这意味着你的服务器需要发送特定的HTTP响应头:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
。如果缺少这些头部,
SharedArrayBuffer
将无法使用,或者在某些浏览器中会退化为普通的
ArrayBuffer

为什么浏览器对SharedArrayBuffer的使用有严格限制?

这其实是个关于安全和性能的权衡。

SharedArrayBuffer
在最初推出时,因为一些安全漏洞(比如著名的Spectre和Meltdown侧信道攻击)而被暂时禁用或限制了。这些攻击可以通过测量CPU执行时间差来推断出内存中的敏感信息,而
SharedArrayBuffer
能够提供高精度计时器,使得这种攻击变得更容易实现。

为了应对这些潜在的风险,浏览器厂商选择对

SharedArrayBuffer
的重新启用施加了严格的条件,即要求页面处于“跨域隔离”状态。这意味着你的页面不能加载任何没有明确允许的跨域资源,从而大幅降低了侧信道攻击的风险。简单来说,就是为了你的用户安全,浏览器强制你把门关严实了,才能用这个强大的工具。如果没有这些头部,浏览器会认为你的环境不够安全,也就不会暴露
SharedArrayBuffer
的能力。

悦灵犀AI
悦灵犀AI

一个集AI绘画、问答、创作于一体的一站式AI工具平台

下载

SharedArrayBuffer与传统数据传递方式(如postMessage)有何不同?

传统上,Web Worker和主线程之间的数据传递主要依赖

postMessage
。当你通过
postMessage
发送一个对象(比如一个大的数组或JSON数据)时,浏览器会对其进行序列化(structured clone algorithm),然后在接收端进行反序列化。这个过程实际上是创建了一个数据的“副本”。对于小数据量来说,这几乎是瞬间完成的,你感觉不到延迟。

然而,当数据量变得非常庞大时,比如几十兆甚至上百兆的图像数据、视频帧或者大型数据集,序列化和反序列化的开销就会变得非常显著。这不仅会消耗大量的CPU资源,还可能导致主线程的卡顿,影响用户体验。

SharedArrayBuffer
则彻底改变了这种模式。它不是传递数据的副本,而是传递对同一块内存的引用。这意味着主线程和Worker操作的都是同一块物理内存。数据不需要被复制,也不需要经过序列化和反序列化,从而避免了这些开销。想象一下,你不再需要把一大箱文件从一个办公室搬到另一个办公室,而是两个同事可以直接在同一个文件柜里翻阅文件。这对于需要高性能计算、复杂数据处理或实时协作的场景来说,是质的飞跃。

使用SharedArrayBuffer时,如何避免数据竞争(Race Condition)问题?

数据竞争是并发编程中一个非常常见且棘手的问题,当多个线程同时访问和修改共享资源(这里就是

SharedArrayBuffer
中的数据)时,操作的顺序不确定,可能导致不可预测的结果或数据损坏。

为了解决这个问题,JavaScript提供了

Atomics
对象。
Atomics
提供了一系列原子操作,这些操作是不可中断的。这意味着当一个线程执行一个原子操作时,其他线程不能同时对同一块内存执行任何操作,直到当前操作完成。这保证了数据的一致性。

以下是一些常用的

Atomics
方法及其用途:

  • Atomics.load(typedArray, index)
    : 原子地读取指定索引的值。
  • Atomics.store(typedArray, index, value)
    : 原子地写入指定索引的值。
  • Atomics.add(typedArray, index, value)
    : 原子地将指定索引的值增加
    value
  • Atomics.sub(typedArray, index, value)
    : 原子地将指定索引的值减少
    value
  • Atomics.compareExchange(typedArray, index, expectedValue, replacementValue)
    : 原子地比较并交换。如果
    typedArray[index]
    的值等于
    expectedValue
    ,则将其替换为
    replacementValue
    。这个方法在实现锁或信号量时非常有用。
  • Atomics.wait(typedArray, index, value, timeout)
    : 让当前线程等待,直到
    typedArray[index]
    的值不再是
    value
    ,或者超时。这是一个阻塞操作,通常用于实现更复杂的同步机制(如生产者-消费者模型)。
  • Atomics.notify(typedArray, index, count)
    : 唤醒在
    typedArray[index]
    上等待的线程。

举个简单的例子,假设我们有一个共享计数器:

// sharedBuffer 是 SharedArrayBuffer,counterArray 是 Int32Array 视图
const counterArray = new Int32Array(sharedBuffer);
counterArray[0] = 0; // 初始化计数器

// 在多个Worker中,如果都直接 counterArray[0]++,就会有竞争问题
// 正确做法是使用 Atomics.add
Atomics.add(counterArray, 0, 1); // 原子地增加计数器

通过

Atomics.add
,无论多少个Worker同时尝试增加计数器,最终的结果都会是正确的,因为每次增加都是一个不可分割的操作。

更复杂的场景,比如一个Worker需要等待另一个Worker完成某项任务才能继续:

// Worker A
// ...执行一些耗时操作...
Atomics.store(statusArray, 0, 1); // 设置状态为“完成”
Atomics.notify(statusArray, 0, Infinity); // 唤醒所有等待的Worker

// Worker B
// ...做一些准备工作...
// 等待 Worker A 完成
const status = Atomics.wait(statusArray, 0, 0); // 如果 statusArray[0] 还是 0,就等待
if (status === 'ok') {
    // Worker A 已经完成,可以继续了
} else if (status === 'timed-out') {
    console.warn('等待超时!');
}

通过

Atomics.wait
Atomics.notify
,你可以构建出生产者-消费者队列、锁机制等更复杂的并发模式,确保共享内存的正确使用,避免了数据不一致和程序崩溃的风险。正确地使用
Atomics
是发挥
SharedArrayBuffer
威力的关键,也是避免引入难以调试的并发bug的根本。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

422

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

537

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

313

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

77

2025.09.10

counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

198

2023.11.20

require的用法
require的用法

require的用法有引入模块、导入类或方法、执行特定任务。想了解更多require的相关内容,可以阅读本专题下面的文章。

466

2023.11.27

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

546

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

546

2023.08.10

go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

30

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
如何进行WebSocket调试
如何进行WebSocket调试

共1课时 | 0.1万人学习

TypeScript全面解读课程
TypeScript全面解读课程

共26课时 | 5.1万人学习

前端工程化(ES6模块化和webpack打包)
前端工程化(ES6模块化和webpack打包)

共24课时 | 5.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号