@@ -1022,7 +1022,7 @@ onmessage = function (ev) {
10221022
10231023线程之间的数据交换可以是各种格式,不仅仅是字符串,也可以是二进制数据。这种交换采用的是复制机制,即一个进程将需要分享的数据复制一份,通过` postMessage ` 方法交给另一个进程。如果数据量比较大,这种通信的效率显然比较低。很容易想到,这时可以留出一块内存区域,由主线程与 Worker 线程共享,两方都可以读写,那么就会大大提高效率,协作起来也会比较简单(不像` postMessage ` 那么麻烦)。
10241024
1025- ES2017 引入[ ` SharedArrayBuffer ` ] ( https://github.com/tc39/ecmascript_sharedmem/blob/master/TUTORIAL.md ) ,允许 Worker 线程与主线程共享同一块内存。` SharedArrayBuffer ` 的 API 与` ArrayBuffer ` 一模一样,唯一的区别是后者无法共享 。
1025+ ES2017 引入[ ` SharedArrayBuffer ` ] ( https://github.com/tc39/ecmascript_sharedmem/blob/master/TUTORIAL.md ) ,允许 Worker 线程与主线程共享同一块内存。` SharedArrayBuffer ` 的 API 与` ArrayBuffer ` 一模一样,唯一的区别是后者无法共享数据 。
10261026
10271027``` javascript
10281028// 主线程
@@ -1157,42 +1157,137 @@ console.log(ia[42]); // 314159
11571157
11581158上面代码中,主线程的` Atomics.store ` 向 42 号位置的赋值,一定是早于 37 位置的赋值。只要 37 号位置等于 163,Worker 线程就不会终止循环,而对 37 号位置和 42 号位置的取值,一定是在` Atomics.load ` 操作之后。
11591159
1160- ** (2)Atomics.wait(),Atomics.wake()**
1160+ 下面是另一个例子。
1161+
1162+ ``` javascript
1163+ // 主线程
1164+ const worker = new Worker (' worker.js' );
1165+ const length = 10 ;
1166+ const size = Int32Array .BYTES_PER_ELEMENT * length;
1167+ // 新建一段共享内存
1168+ const sharedBuffer = new SharedArrayBuffer (size);
1169+ const sharedArray = new Int32Array (sharedBuffer);
1170+ for (let i = 0 ; i < 10 ; i++ ) {
1171+ // 向共享内存写入 10 个整数
1172+ Atomics .store (sharedArray, i, 0 );
1173+ }
1174+ worker .postMessage (sharedBuffer);
1175+ ```
1176+
1177+ 上面代码中,主线程用` Atomics.store() ` 方法写入数据。下面是 Worker 线程用` Atomics.load() ` 方法读取数据。
1178+
1179+ ``` javascript
1180+ // worker.js
1181+ self .addEventListener (' message' , (event ) => {
1182+ const sharedArray = new Int32Array (event .data );
1183+ for (let i = 0 ; i < 10 ; i++ ) {
1184+ const arrayValue = Atomics .load (sharedArray, i);
1185+ console .log (` The item at array index ${ i} is ${ arrayValue} ` );
1186+ }
1187+ }, false );
1188+ ```
1189+
1190+ ** (2)Atomics.exchange()**
1191+
1192+ Worker 线程如果要写入数据,可以使用上面的` Atomics.store() ` 方法,也可以使用` Atomics.exchange() ` 方法。它们的区别是,` Atomics.store() ` 返回写入的值,而` Atomics.exchange() ` 返回被替换的值。
1193+
1194+ ``` javascript
1195+ // Worker 线程
1196+ self .addEventListener (' message' , (event ) => {
1197+ const sharedArray = new Int32Array (event .data );
1198+ for (let i = 0 ; i < 10 ; i++ ) {
1199+ if (i % 2 === 0 ) {
1200+ const storedValue = Atomics .store (sharedArray, i, 1 );
1201+ console .log (` The item at array index ${ i} is now ${ storedValue} ` );
1202+ } else {
1203+ const exchangedValue = Atomics .exchange (sharedArray, i, 2 );
1204+ console .log (` The item at array index ${ i} was ${ exchangedValue} , now 2` );
1205+ }
1206+ }
1207+ }, false );
1208+ ```
1209+
1210+ 上面代码将共享内存的偶数位置的值改成` 1 ` ,奇数位置的值改成` 2 ` 。
1211+
1212+ ** (3)Atomics.wait(),Atomics.wake()**
11611213
11621214使用` while ` 循环等待主线程的通知,不是很高效,如果用在主线程,就会造成卡顿,` Atomics ` 对象提供了` wait() ` 和` wake() ` 两个方法用于等待通知。这两个方法相当于锁内存,即在一个线程进行操作时,让其他线程休眠(建立锁),等到操作结束,再唤醒那些休眠的线程(解除锁)。
11631215
11641216``` javascript
1165- Atomics .wait (sharedArray, index, value, time)
1217+ // Worker 线程
1218+ self .addEventListener (' message' , (event ) => {
1219+ const sharedArray = new Int32Array (event .data );
1220+ const arrayIndex = 0 ;
1221+ const expectedStoredValue = 50 ;
1222+ Atomics .wait (sharedArray, arrayIndex, expectedStoredValue);
1223+ console .log (Atomics .load (sharedArray, arrayIndex));
1224+ }, false );
1225+ ```
1226+
1227+ 上面代码中,` Atomics.wait() ` 方法等同于告诉 Worker 线程,只要满足给定条件(` sharedArray ` 的` 0 ` 号位置等于` 50 ` ),就在这一行 Worker 线程进入休眠。
1228+
1229+ 主线程一旦更改了指定位置的值,就可以唤醒 Worker 线程。
1230+
1231+ ``` javascript
1232+ // 主线程
1233+ const newArrayValue = 100 ;
1234+ Atomics .store (sharedArray, 0 , newArrayValue);
1235+ const arrayIndex = 0 ;
1236+ const queuePos = 1 ;
1237+ Atomics .wake (sharedArray, arrayIndex, queuePos);
1238+ ```
1239+
1240+ 上面代码中,` sharedArray ` 的` 0 ` 号位置改为` 100 ` ,然后就执行` Atomics.wake() ` 方法,唤醒在` sharedArray ` 的` 0 ` 号位置休眠队列里的一个线程。
1241+
1242+ ` Atomics.wait() ` 方法的使用格式如下。
1243+
1244+ ``` javascript
1245+ Atomics .wait (sharedArray, index, value, timeout)
11661246```
11671247
1168- ` Atomics.wait ` 用于当` sharedArray[index] ` 不等于` value ` ,就返回` not-equal ` ,否则就进入休眠,只有使用` Atomics.wake() ` 或者` time ` 毫秒以后才能唤醒。被` Atomics.wake() ` 唤醒时,返回` ok ` ,超时唤醒时返回` timed-out ` 。
1248+ 它的四个参数含义如下。
1249+
1250+ - sharedArray:共享内存的视图数组。
1251+ - index:视图数据的位置(从0开始)。
1252+ - value:该位置的预期值。一旦实际值等于预期值,就进入休眠。
1253+ - timeout:整数,表示过了这个时间以后,就自动唤醒,单位毫秒。该参数可选,默认值是` Infinity ` ,即无限期的休眠,只有通过` Atomics.wake() ` 方法才能唤醒。
1254+
1255+ ` Atomics.wait() ` 的返回值是一个字符串,共有三种可能的值。如果` sharedArray[index] ` 不等于` value ` ,就返回字符串` not-equal ` ,否则就进入休眠。如果` Atomics.wake() ` 方法唤醒,就返回字符串` ok ` ;如果因为超时唤醒,就返回字符串` timed-out ` 。
1256+
1257+ ` Atomics.wake() ` 方法的使用格式如下。
11691258
11701259``` javascript
11711260Atomics .wake (sharedArray, index, count)
11721261```
11731262
1174- ` Atomics.wake ` 用于唤醒` count ` 数目在` sharedArray[index] ` 位置休眠的线程,让它继续往下运行。
1263+ 它的三个参数含义如下。
1264+
1265+ - sharedArray:共享内存的视图数组。
1266+ - index:视图数据的位置(从0开始)。
1267+ - count:需要唤醒的 Worker 线程的数量,默认为` Infinity ` 。
1268+
1269+ ` Atomics.wake() ` 方法一旦唤醒休眠的 Worker 线程,就会让它继续往下运行。
11751270
1176- 下面请看一个例子 。
1271+ 请看一个例子 。
11771272
11781273``` javascript
1179- // 线程一
1274+ // 主线程
11801275console .log (ia[37 ]); // 163
11811276Atomics .store (ia, 37 , 123456 );
11821277Atomics .wake (ia, 37 , 1 );
11831278
1184- // 线程二
1279+ // Worker 线程
11851280Atomics .wait (ia, 37 , 163 );
11861281console .log (ia[37 ]); // 123456
11871282```
11881283
1189- 上面代码中,共享内存视图 ` ia ` 的第 37 号位置,原来的值是` 163 ` 。进程二使用 ` Atomics.wait() ` 方法,指定只要` ia[37] ` 等于` 163 ` ,就进入休眠状态。进程一使用 ` Atomics.store() ` 方法,将` 123456 ` 放入 ` ia[37] ` ,然后使用` Atomics.wake() ` 方法将监视 ` ia[37] ` 的休眠线程唤醒 。
1284+ 上面代码中,视图数组 ` ia ` 的第 37 号位置,原来的值是` 163 ` 。Worker 线程使用 ` Atomics.wait() ` 方法,指定只要` ia[37] ` 等于` 163 ` ,就进入休眠状态。主线程使用 ` Atomics.store() ` 方法,将` 123456 ` 写入 ` ia[37] ` ,然后使用` Atomics.wake() ` 方法唤醒 Worker 线程 。
11901285
11911286另外,基于` wait ` 和` wake ` 这两个方法的锁内存实现,可以看 Lars T Hansen 的 [ js-lock-and-condition] ( https://github.com/lars-t-hansen/js-lock-and-condition ) 这个库。
11921287
1193- 注意,浏览器的主线程有权“拒绝”休眠,这是为了防止用户失去响应 。
1288+ 注意,浏览器的主线程不宜设置休眠,这会导致用户失去响应。而且,主线程实际上会拒绝进入休眠 。
11941289
1195- ** (3 )运算方法**
1290+ ** (4 )运算方法**
11961291
11971292共享内存上面的某些运算是不能被打断的,即不能在运算过程中,让其他线程改写内存上面的值。Atomics 对象提供了一些运算方法,防止数据被改写。
11981293
@@ -1226,12 +1321,11 @@ Atomics.xor(sharedArray, index, value)
12261321
12271322` Atomic.xor ` 用于将` vaule ` 与` sharedArray[index] ` 进行位运算` xor ` ,放入` sharedArray[index] ` ,并返回旧的值。
12281323
1229- ** (4 )其他方法**
1324+ ** (5 )其他方法**
12301325
12311326` Atomics ` 对象还有以下方法。
12321327
12331328- ` Atomics.compareExchange(sharedArray, index, oldval, newval) ` :如果` sharedArray[index] ` 等于` oldval ` ,就写入` newval ` ,返回` oldval ` 。
1234- - ` Atomics.exchange(sharedArray, index, value) ` :设置` sharedArray[index] ` 的值,返回旧的值。
12351329- ` Atomics.isLockFree(size) ` :返回一个布尔值,表示` Atomics ` 对象是否可以处理某个` size ` 的内存锁定。如果返回` false ` ,应用程序就需要自己来实现锁定。
12361330
12371331` Atomics.compareExchange ` 的一个用途是,从 SharedArrayBuffer 读取一个值,然后对该值进行某个操作,操作结束以后,检查一下 SharedArrayBuffer 里面原来那个值是否发生变化(即被其他线程改写过)。如果没有改写过,就将它写回原来的位置,否则读取新的值,再重头进行一次操作。
0 commit comments