在过去,Web 应用的开发主要是单线程的,这意味着 JavaScript 执行环境只有一个线程,也就是说只能同步执行代码。但是,随着 Web 应用变得越来越庞大,单线程的弊端也越来越明显,将会阻断用户的体验,并且在大处理量的任务下很难有效地使用利用 CPU 和硬件资源。为了解决这些问题,多线程技术被引入到 ES8 中,SharedArrayBuffer 就是其中之一。
SharedArrayBuffer 概述
在多线程应用中,各线程共享同一段内存,而 SharedArrayBuffer 是一种新型的 JavaScript 对象,它提供了共享内存的方式,使得多线程能够更高效地工作。它可以被所有线程所共享,并且当一个线程写入了 SharedArrayBuffer 中的数据,其余线程可以立即看到更新的值,从而实现最快的共享内存。
SharedArrayBuffer 对象的全局变量名为 SharedArrayBuffer,其构造函数为 new SharedArrayBuffer(),使用方式和 ArrayBuffer 相似。
下面是一个简单的 SharedArrayBuffer 示例:
const sab = new SharedArrayBuffer(8); const sharedArray = new Int32Array(sab);
在这个例子中,我们初始化了一个 4 字节的 SharedArrayBuffer(8 个字节),并将其作为参数输出到 Int32Array 构造函数中,将其转换为一个 32 位数组。现在,操作这个数组的所有线程都可以自由地读取和写入,而且可以保证线程的操作是基于相同数据的。
SharedArrayBuffer 中的原子操作
当多线程应用程序使用共享内存时,必须采取一些特殊的措施,以确保不会导致数据损坏或破坏共享内存的完整性。ES8 提供了原子操作的支持,其中的各个操作原子地执行,以避免在读取和写入共享内存时发生竞态(race conditions)。所有的原子操作都要求在操作开始时给定合适的内存参数和合法的数据长度。
下面是一些常用的原子操作:
- Atomics.add():原子加操作,将给定值相加并返回结果。
- Atomics.and():原子与操作,将给定值与对应值按位相与并返回结果。
- Atomics.exchange():以原子方式替换数组中指定索引的值,并返回原始值。
- Atomics.wait():等待某个值与数组中指定的索引处的值相等,并返回“ok”或“not-equal”。
- Atomics.notify():通知正在等待等待某个值与数组中指定的索引处的值相等的线程。
在接下来的代码示例中,我们将演示如何使用原子操作来执行一些基本的操作。
const sab = new SharedArrayBuffer(8); const sharedArray = new Int32Array(sab); Atomics.add(sharedArray, 0, 1); Atomics.sub(sharedArray, 1, 3); Atomics.exchange(sharedArray, 2, 10); console.log(sharedArray);
在这个示例中,我们初始值设为全为 0,然后使用 Atomics.add() 和 Atomics.sub() 进行累加和减少。最后,我们使用 Atomics.exchange() 替换数组中的索引 2,从而输出新的数组。
使用 SharedArrayBuffer 构建多线程应用程序
使用 SharedArrayBuffer 和 Atomics 构建多线程应用程序的过程并不像单线程应用程序那样直接。为了实现多线程应用程序,我们需要使用 workers。
Web Worker 是运行在后台的 JavaScript 脚本,其目的是为了开发 Web 应用程序,可以让 JavaScript 在单独的线程上运行。每个 Worker 都有一个高度私有化的作用域,独立于其他页面上的内存。
下面是一个使用 SharedArrayBuffer 和 Atomics 的基本代码示例:
index.html:
-- -------------------- ---- ------- --------- ----- ------ ------ -------- ----- ------ - --- -------------------- ---------------- - -------- ------- - ----- ----------- - --- ----------------------- ------------------------- -- --------- ------- ------ ------- -------
worker.js:
-- -------------------- ---- ------- --------- - -------- ------- - ----- --- - ----------- ----- ----------- - --- ---------------- ------------------------ -- --- ------------------------ -- --- ----------------------------- -- ---- -------------------------------- --
在这个示例中,我们创建了 Worker,并向其发送一个 SharedArrayBuffer。在 worker.js 中,我们将 SharedArrayBuffer 转换为一个 Int32Array,并使用原子操作方式操作其中的数据。最后,我们使用 postMessage 发送处理完的 SharedArrayBuffer。
总结
SharedArrayBuffer & Atomics 是 ES8 中重要的新增特性,它们为我们提供了构建高性能多线程应用程序的机会。共享内存的使用意味着多个线程在读取和写入共享内存时发生竞当问题,为解决这些问题,ES8 引入了原子操作。在使用 SharedArrayBuffer 和 Atomics 之前,我们需要了解一定的底层知识和控制,并小心地使用这些特性以避免可能发生的问题。
我们需要谨慎选择 SharedArrayBuffer 和 Atomics 这些概念。在某些应用场景下,使用多线程可能会导致不必要的复杂性,并导致场景糟糕。但在其他场景下,多线程是迫切需要的,因为它可以显着提升应用程序的性能。通过权衡利弊,并谨慎使用 SharedArrayBuffer 和 Atomics,我们可以进一步拓展 Web 应用程序的潜力。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/647eecb848841e9894e9b53e