JavaScript中的SharedArrayBuffer
本文将解释JavaScript中的SharedArrayBuffer。
我们将详细讲解 SharedArrayBuffer 的基础知识、使用方法、具体使用场景以及安全考虑。
YouTube Video
JavaScript中的SharedArrayBuffer
SharedArrayBuffer是JavaScript中用于在多个线程之间共享内存的强大工具。特别是结合Web Workers使用时,它支持并行处理,使其在处理计算密集型任务和需要实时能力的应用中非常高效。
什么是SharedArrayBuffer?
SharedArrayBuffer在JavaScript中提供了一种内存缓冲区,允许在多个线程之间共享二进制数据(主要是Web Workers)。普通的ArrayBuffer需要在线程主线程和工作线程之间进行复制,而SharedArrayBuffer则允许直接内存共享,无需复制,从而显著提升了性能。
功能
- 共享内存 它允许多个线程在同一内存空间中工作。
- 性能提升 由于可以省略复制,在处理大量数据时可减少开销。
- 线程同步
可以与
Atomics一起使用进行同步,以避免访问内存时发生冲突。
基本使用示例
1// Create a 16-byte shared memory
2const sharedBuffer = new SharedArrayBuffer(16);
3
4// Treat it as an Int32Array
5const sharedArray = new Int32Array(sharedBuffer);
6
7// Set a value
8sharedArray[0] = 42;
9
10console.log(sharedArray[0]); // 42
在这个示例中,我们使用SharedArrayBuffer创建一个16字节的内存区,并将该内存区作为Int32Array处理。该内存缓冲区可以在多个线程之间共享。
与Web Workers结合使用
当与Web Workers结合使用时,SharedArrayBuffer的真正价值得以体现。以下代码是主线程和工作线程之间使用共享内存的示例。
在主线程上
1// Create a shared buffer
2const sharedBuffer = new SharedArrayBuffer(16);
3const sharedArray = new Int32Array(sharedBuffer);
4
5// Create a worker
6const worker = new Worker('worker.js');
7
8// Pass the shared buffer to the worker
9worker.postMessage(sharedBuffer);
10
11// Modify the memory
12// Output : Main thread: 100
13sharedArray[0] = 100;
14console.log("Main thread: ", sharedArray[0]);在工作线程端(worker.js)
1// worker.js
2self.onmessage = function(event) {
3 // Use the received shared buffer
4 const sharedArray = new Int32Array(event.data);
5
6 // Read the contents of the memory
7 // Output : Worker thread: 100
8 console.log("Worker thread: ", sharedArray[0]);
9
10 // Change the value
11 sharedArray[0] = 200;
12};- 在这个示例中,主线程创建了一个共享缓冲区并将其传递给工作线程。工作线程可以访问该缓冲区以读取和修改值。通过这种方式,可以在线程之间共享数据而无需复制。
双向更新确认
通过使用 SharedArrayBuffer,主线程和工作线程都可以读写同一块内存,实现双向更新确认。以下是一个示例:主线程设置一个值,工作线程修改该值,然后主线程检查更新。
在主线程上
1// Create a shared buffer
2const sharedBuffer = new SharedArrayBuffer(16);
3const sharedArray = new Int32Array(sharedBuffer);
4
5// Create a worker
6const worker = new Worker('worker.js');
7
8// Pass the shared buffer to the worker
9worker.postMessage(sharedBuffer);
10
11// Set initial value
12// Output : Main thread initial: 100
13sharedArray[0] = 100;
14console.log("Main thread initial:", sharedArray[0]);
15
16// Listen for worker confirmation
17worker.onmessage = () => {
18 // Output : Main thread after worker update: 200
19 console.log("Main thread after worker update:", sharedArray[0]);
20};在工作线程端(worker.js)
1// worker.js
2self.onmessage = function(event) {
3 const sharedArray = new Int32Array(event.data);
4
5 // Read initial value
6 // Output : Worker thread received: 100
7 console.log("Worker thread received:", sharedArray[0]);
8
9 // Update the value
10 sharedArray[0] = 200;
11
12 // Notify main thread
13 self.postMessage("Value updated");
14};- 在此示例中,主线程首先写入值
100,工作线程读取后将其改为200。之后,工作线程通知主线程,主线程再次读取共享内存以确认更新。通过结合通知,可以实现双向更新确认。
使用Atomics进行同步
在使用共享内存时,必须注意数据竞争条件和不一致性。当多个线程同时访问相同的内存时,可能会发生冲突。为防止这种情况,JavaScript 使用 Atomics 对象进行同步。
例如,为了在多个线程中安全地递增一个计数器,可以使用 Atomics 防止冲突。
1const sharedBuffer = new SharedArrayBuffer(16);
2const sharedArray = new Int32Array(sharedBuffer);
3
4// Increment the counter
5Atomics.add(sharedArray, 0, 1);
6
7console.log(Atomics.load(sharedArray, 0)); // 1
Atomics.add 可以以原子操作的方式递增特定索引处的值,并返回新值。此操作可以确保与其他线程之间无冲突。Atomics.load 也被用于安全地从共享内存中读取值。
使用 Atomics.wait 和 Atomics.notify 进行等待与通知
在使用 SharedArrayBuffer 时,某些情况下需要让一个工作线程等待某个条件成立,而当另外一个线程满足条件后,需要通知正在等待的线程。此时,Atomics.wait 和 Atomics.notify 就非常有用。
Atomics.wait 会让线程等待直到共享内存中特定索引的值发生变化,而 Atomics.notify 会通知等待中的线程可以继续执行。这样可以在多个工作线程之间安全地实现等待与通知。但是,Atomics.wait 不能在主线程中使用,只能在 worker 内部使用。
1// Create a shared buffer (1 Int32 slot is enough for signaling)
2const sharedBuffer = new SharedArrayBuffer(4);
3const sharedArray = new Int32Array(sharedBuffer);
4
5// Create workers with names
6const waiter = new Worker('worker.js', { name: 'waiter' });
7const notifier = new Worker('worker.js', { name: 'notifier' });
8
9// Pass the shared buffer to both
10waiter.postMessage(sharedBuffer);
11notifier.postMessage(sharedBuffer);
12
13// Listen for messages
14waiter.onmessage = (event) => {
15 console.log(`[Main] Message from waiter:`, event.data);
16};
17notifier.onmessage = (event) => {
18 console.log(`[Main] Message from notifier:`, event.data);
19};- 在主线程中,创建了一个
SharedArrayBuffer作为共享内存,并将其转换为只包含一个元素的Int32Array。这个单一的整数槽用作在 worker 之间同步的信号。接下来,创建了两个 worker,并通过name属性为每个 worker 分配了一个角色:waiter(等待者)和notifier(通知者)。最后,将共享缓冲区传递给两个 worker,并设置了onmessage处理程序,以便能够接收来自每个 worker 的消息。
在工作线程端(worker.js)
1// worker.js
2onmessage = (event) => {
3 const sharedArray = new Int32Array(event.data);
4
5 if (self.name === 'waiter') {
6 postMessage('Waiter is waiting...');
7 // Wait until notifier signals index 0
8 Atomics.wait(sharedArray, 0, 0);
9 postMessage('Waiter was notified!');
10 }
11
12 if (self.name === 'notifier') {
13 postMessage('Notifier is preparing...');
14 setTimeout(() => {
15 // Notify waiter after 2 seconds
16 Atomics.store(sharedArray, 0, 1);
17 Atomics.notify(sharedArray, 0, 1);
18 postMessage('Notifier has sent the signal!');
19 }, 2000);
20 }
21};
22// Output
23// [Main] Message from waiter: Waiter is waiting...
24// [Main] Message from notifier: Notifier is preparing...
25// [Main] Message from notifier: Notifier has sent the signal!
26// [Main] Message from waiter: Waiter was notified!
- 在此示例中,只要索引为
0的值为0,waiterworker 就会使用Atomics.wait保持等待状态。另一方面,当notifierworker 使用Atomics.store将值更改为123并调用Atomics.notify后,waiterworker 会恢复并能够获取到更新后的值。通过这种方式,可以实现高效且安全的线程间等待与通知。
SharedArrayBuffer 的使用场景
SharedArrayBuffer 特别适用于以下使用场景:。
- 实时处理 适用于需要低延迟的应用场景,如音视频处理或游戏引擎,需要在线程之间实时共享数据。
- 并行计算
在多个线程同时处理大量数据时,使用
SharedArrayBuffer可以避免内存复制,提高性能。 - 机器学习 通过并行化数据预处理和推断等任务,可以实现高效计算。
安全性考虑
SharedArrayBuffer 是一个强大的功能,但也存在安全风险。特别是对像 Spectre 这样的侧信道攻击的担忧,导致其支持一度被暂时中止。为了缓解这一漏洞,浏览器已经实施了以下措施:。
- 站点隔离
允许使用
SharedArrayBuffer的站点将运行在与其他站点完全隔离的进程中。 - 跨源资源策略
要使用
SharedArrayBuffer,必须正确设置Cross-Origin-Opener-Policy和Cross-Origin-Embedder-Policy头信息。
例如,通过设置如下头信息,可以启用 SharedArrayBuffer 的使用:。
1Cross-Origin-Opener-Policy: same-origin
2Cross-Origin-Embedder-Policy: require-corp这可以防止外部资源干扰当前内容,并提高安全性。
总结
SharedArrayBuffer 是一个在多线程之间共享内存的非常强大的工具。这是提高性能的一项重要技术,其效果在实时处理和并行计算领域尤为显著。然而,这也涉及到安全风险,因此正确的配置和同步非常重要。
通过利用 SharedArrayBuffer,可以构建更加先进和高性能的 Web 应用程序。
您可以在我们的YouTube频道上使用Visual Studio Code跟随上述文章进行学习。 请也查看我们的YouTube频道。