在传统的 JavaScript 环境中,每一个操作都是独立的,且只能在主线程中执行。但在现代浏览器中,Web Worker 能够让我们在后台线程中执行一些重量级的操作,从而避免阻塞主线程。不过,这种操作仍然受到了限制:不同线程之间无法进行共享内存的操作。
不过,随着 ECMAScript 2017 的发布,现在已经支持了 Shared Memory和 Atomics 两个新特性,它们能让 JavaScript 实现更高效的多线程编程。本文将对 Shared Memory 和 Atomics 进行详细解释,并提供一些例子来展示它们的使用。
Shared Memory
Shared Memory 可以让不同的线程之间共享一段存储空间,这样它们就可以通过读写该空间中的数据来进行通信。Shared Memory 可以极大地提高多线程程序中的效率,因为数据不必进出主内存,从而降低了通信的开销。
Shared Memory 是由 ArrayBuffer,SharedArrayBuffer 和 TypedArray 三个构造函数组成的。这三个构造函数定义了多个 JavaScript 类型,它们可以让我们摆脱过去使用数组和对象进行大规模数据传输时的低效问题。
下面是一个简单的例子,展示了如何使用 SharedArrayBuffer 和 TypedArray:
-- -------------------- ---- ------- -- --------- ---------------------- ---------- ----- ------------ - --- --------------------- ----- ---- - --- ------------------------- ----- ---- - --- ------------------------- -- ------- ---- ------- ------------- ---- ------ ------- - --- --- --------------------------------- ------- ------------ --- -- - --------- - --------- - -- ----- - ------ - -- -- - ----- --- - --- ------------------- -------------------- --
在该示例中,我们在主线程中创建了一个 SharedArrayBuffer,包含了 4 个 Int32Array 元素。我们还创建了两个 Int32Array,arr1 和 arr2,它们所共享的是同一个 SharedArrayBuffer。主线程设置 arr1 的第一个元素为 42,然后将 SharedArrayBuffer 传递到子线程中。
在子线程中,我们将 ArrayBuffer 转换成了 Int32Array,并打印出了 arr2 的第一个元素。由于 arr2 与 arr1 共享了同一个存储区域,所以打印出的值是 42。
Atomics
Atomics 可以让我们在共享内存中执行原子操作,以保证多个线程之间的通信协调性。虽然 Shared Memory 可以让多个线程同时对一个数据进行操作,但如果同时对该数据执行多个操作,就有可能产生竞争条件,而 Atomics 则能够解决这个问题。
在 Atomics 中,提供了一些用来执行原子操作的函数。这些函数是原子的,表示在执行它们时不会被打断。由于这些原子操作是不可中断的,所以可以保证数据的一致性。
下面是一个修改 SharedArrayBuffer 中的值,并使用 Atomics 来保证原子性的例子:
-- -------------------- ---- ------- -- --------- ---------------------- ---------- ----- ------------ - --- --------------------- ----- --- - --- ------------------------- -- --------- --- ------------------ ------- ------ --- ---------------------------------- ------- ------------ --- --- ---------------------------------- ------- ------------ --- -- ---------- --------- - -- ----- - ------ - -- -- - ----- --- - --- ------------------- --- ---- - - -- - - ---- ---- - ---------------- -- --- - -- -- ---------- --------- - -- ----- - ------ - -- -- - ----- --- - --- ------------------- --- ---- - - -- - - ---- ---- - ---------------- -- --- - --
在该示例中,我们首先创建了一个 SharedArrayBuffer 和一个 Int32Array,然后将它们传递到两个 worker 中。在每个 worker 中,我们对 SharedArrayBuffer 的第一个元素进行自增操作。在这两个 worker 中,Atomics.add() 可以保证对于同一个元素的自增操作是原子的。
结论
Shared Memory 和 Atomics 可以让我们在 JavaScript 中实现高效的多线程编程。如果你在编写需要大量计算的 JavaScript 应用程序,那么这两个新特性的到来将会给你的应用程序带来极大的性能提升。
不过,当使用 Shared Memory 和 Atomics 时需要小心谨慎,因为这些功能需要开发者自己来维护线程之间的同步和协调。与共享内存一样,需要注意数据的一致性问题,以避免意外修改数据的情况发生。
最后值得一提的是,不是所有浏览器都支持 Shared Memory 和 Atomics,请在使用之前确保浏览器已经支持这些特性。
参考资料:
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67510cb3050cf9039c19a5e4