在这个信息化时代,越来越多的应用程序需要同时处理众多数据,甚至跨多个应用程序之间协作完成任务。在这种情况下,多线程技术就显得尤为重要。在传统的前端开发中,由于 JavaScript 单线程的特性,多线程编程都是通过 Web Workers 来实现,但 Web Workers 的缺点也是比较明显的:不能在多个线程之间直接共享内存空间,这样可能导致内存浪费和多线程同步问题。ES9 中的 SharedArrayBuffer 和 Atomics 提供了一种新的方法来解决这个问题,为前端多线程带来了新的工具。
SharedArrayBuffer
SharedArrayBuffer 是一种新类型的 ArrayBuffer,它可以在所有的共享的 Worker、ServiceWorker 或 iframe 中使用,并且可以在这些线程之间直接共享内存空间。
对于共享内存空间的使用,关键的问题是如何协调多个线程对于同一块内存的访问。这将涉及到线程同步和竞争条件等问题,后文将介绍 Atomics 来解决这些问题。
SharedArrayBuffer 的使用方法与 ArrayBuffer 非常相似,只需要将类型从 ArrayBuffer 替换为 SharedArrayBuffer,例如:
const sab = new SharedArrayBuffer(1024); const intArray = new Int32Array(sab);
上述代码中,首先创建了一个长度为 1024 字节的 SharedArrayBuffer,然后通过 Int32Array 视图创建了一个 32 位整数类型的数组。这样,在所有共享同一个 SharedArrayBuffer 的线程中,都可以直接访问到 intArray 数组中的数据,从而实现了数据的共享。
需要注意的是,由于 SharedArrayBuffer 直接共享内存的特性,为了保证安全性,对 SharedArrayBuffer 的访问是基于线程间所使用的同一种源或文档来进行的,这样可以保证共享内存的各种操作都是可控的。
Atomics
在多线程编程中,一个常见的场景就是需要同步多个线程对于共享数据的读写。如果没有同步和协调,就很容易出现类似竞争条件、死锁这样的问题,这会使程序出现非常严重的错误。
Atomics 对象提供了一套原子性的操作,这些操作可以对多个线程并发访问的 SharedArrayBuffer 中的数据进行操作,并且保证这些操作的原子性,从而避免了线程同步和竞争条件等问题。
在使用 Atomics API 时,需要注意以下几点:
- Atomics 操作的目标必须是 SharedArrayBuffer 中的一个元素
- Atomics 操作的结果是原子性的,不会被其他线程中断或干扰
- Atomics 操作与 JavaScript 的 Event Loop 基本无关,要考虑它们之间的关系需要特别注意
下面是 Atomics 引入的一些操作:
add
将 SharedArrayBuffer 中的指定位置中的值与给定的值相加,并返回相加后的新值。
Atomics.add(sharedArrayBuffer, index, value);
示例代码:
-- -------------------- ---- ------- --- ----------------- - --- ------------------------ --- ---------- - --- ------------------------------ --- ----- - --- -- -- -- --- --- ---- - - -- - - ------------- ---- - ----------------------- -- ---------- - ------------------------ -- ---------- - -- -- -- --- --- -- -- --- -
and
将 SharedArrayBuffer 中的指定位置中的值与给定的值进行按位与(&)运算,并返回运算后的新值。
Atomics.and(sharedArrayBuffer, index, value);
示例代码:
-- -------------------- ---- ------- --- ----------------- - --- ------------------------ --- ---------- - --- ------------------------------ --- ---- - - -- - - ------------------ ---- - ------------------------- -- ---- - ----------------------- -- --- ------------------------ -- ---------- - --- -- --- --- --- --- --- --- -
load
返回 SharedArrayBuffer 中指定位置的值。
Atomics.load(sharedArrayBuffer, index);
示例代码:
var sharedArrayBuffer = new SharedArrayBuffer(1024); var int32Array = new Int32Array(sharedArrayBuffer); Atomics.store(int32Array, 0, 100); console.log(Atomics.load(int32Array, 0)); // 100
or
将 SharedArrayBuffer 中的指定位置中的值与给定的值进行按位或(|)运算,并返回运算后的新值。
Atomics.or(sharedArrayBuffer, index, value);
示例代码:
-- -------------------- ---- ------- --- ----------------- - --- ------------------------ --- ---------- - --- ------------------------------ --- ---- - - -- - - ------------------ ---- - ------------------------- -- ---- - ---------------------- -- --- ------------------------ -- ---------- - --- --- --- --- --- --- --- --- -
store
将特定值存储到 SharedArrayBuffer 中指定位置,无需查询原位置的值。
Atomics.store(sharedArrayBuffer, index, value);
示例代码:
var sharedArrayBuffer = new SharedArrayBuffer(1024); var int32Array = new Int32Array(sharedArrayBuffer); Atomics.store(int32Array, 0, 100); console.log(int32Array); // Int32Array [ 100, 0, 0, 0, 0, 0, ... ]
sub
将 SharedArrayBuffer 中的指定位置中的值与给定的值相减,并返回相减后的新值。
Atomics.sub(sharedArrayBuffer, index, value);
示例代码:
-- -------------------- ---- ------- --- ----------------- - --- ------------------------ --- ---------- - --- ------------------------------ --- ---- - - -- - - ------------------ ---- - ------------------------- -- ---- - ----------------------- -- --- ------------------------ -- ---------- - --- -- --- --- --- --- --- --- -
xor
将 SharedArrayBuffer 中的指定位置中的值与给定的值进行按位异或(^)运算,并返回运算后的新值。
Atomics.xor(sharedArrayBuffer, index, value);
示例代码:
-- -------------------- ---- ------- --- ----------------- - --- ------------------------ --- ---------- - --- ------------------------------ --- ---- - - -- - - ------------------ ---- - ------------------------- -- ---- - ----------------------- -- --- ------------------------ -- ---------- - --- -- --- --- --- --- --- --- -
总结
在 ES9 中,SharedArrayBuffer 和 Atomics 是 JavaScript 多线程实现的新工具。SharedArrayBuffer 提供了一种直接共享内存的方式,而 Atomics 提供了原子性操作,可确保多线程程序的线程同步和竞争条件等问题被解决。虽然 ES9 中的多线程功能仍然比较基础,但这对于大多数前端开发者来说是一个重大的进步,可以让他们更好地处理高并发数据场景。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6647d72fd3423812e4661beb