@@ -975,59 +975,65 @@ bitmap.pixels = new Uint8Array(buffer, start);
975
975
976
976
## SharedArrayBuffer
977
977
978
- JavaScript 是单线程的,web worker 引入了多进程,每个进程的数据都是隔离的 ,通过` postMessage() ` 通信,即通信的数据是复制的。如果数据量比较大,这种通信的效率显然比较低 。
978
+ JavaScript 是单线程的,Web worker 引入了多线程:主线程用来与用户互动,Worker 线程用来承担计算任务。每个线程的数据都是隔离的 ,通过` postMessage() ` 通信。下面是一个例子 。
979
979
980
980
``` javascript
981
+ // 主线程
981
982
var w = new Worker (' myworker.js' );
982
983
```
983
984
984
- 上面代码中,主进程新建了一个 Worker 进程。该进程与主进程之间会有一个通信渠道,主进程通过 ` w.postMessage ` 向 Worker 进程发消息 ,同时通过` message ` 事件监听 Worker 进程的回应 。
985
+ 上面代码中,主线程新建了一个 Worker 线程。该线程与主线程之间会有一个通信渠道,主线程通过 ` w.postMessage ` 向 Worker 线程发消息 ,同时通过` message ` 事件监听 Worker 线程的回应 。
985
986
986
987
``` javascript
988
+ // 主线程
987
989
w .postMessage (' hi' );
988
990
w .onmessage = function (ev ) {
989
991
console .log (ev .data );
990
992
}
991
993
```
992
994
993
- 上面代码中,主进程先发一个消息 ` hi ` ,然后在监听到 Worker 进程的回应后 ,就将其打印出来。
995
+ 上面代码中,主线程先发一个消息 ` hi ` ,然后在监听到 Worker 线程的回应后 ,就将其打印出来。
994
996
995
- Worker 进程也是通过监听 ` message ` 事件,来获取主进程发来的消息 ,并作出反应。
997
+ Worker 线程也是通过监听 ` message ` 事件,来获取主线程发来的消息 ,并作出反应。
996
998
997
999
``` javascript
1000
+ // Worker 线程
998
1001
onmessage = function (ev ) {
999
1002
console .log (ev .data );
1000
1003
postMessage (' ho' );
1001
1004
}
1002
1005
```
1003
1006
1004
- 主进程与 Worker 进程之间,可以传送各种数据, 不仅仅是字符串,还可以传送二进制数据。很容易想到,如果有大量数据要传送,留出一块内存区域,主进程与 Worker 进程共享 ,两方都可以读写,那么就会大大提高效率。
1007
+ 线程之间的数据交换可以是各种格式, 不仅仅是字符串,也可以是二进制数据。这种交换采用的是复制机制,即一个进程将需要分享的数据复制一份,通过 ` postMessage ` 方法交给另一个进程。如果数据量比较大,这种通信的效率显然比较低。很容易想到,这时可以留出一块内存区域,由主线程与 Worker 线程共享 ,两方都可以读写,那么就会大大提高效率,协作起来也会比较简单(不像 ` postMessage ` 那么麻烦) 。
1005
1008
1006
- ES2017 引入[ ` SharedArrayBuffer ` ] ( https://github.com/tc39/ecmascript_sharedmem/blob/master/TUTORIAL.md ) ,允许多个 Worker 进程与主进程共享内存数据 。` SharedArrayBuffer ` 的 API 与` ArrayBuffer ` 一模一样,唯一的区别是后者无法共享。
1009
+ ES2017 引入[ ` SharedArrayBuffer ` ] ( https://github.com/tc39/ecmascript_sharedmem/blob/master/TUTORIAL.md ) ,允许 Worker 线程与主线程共享同一块内存 。` SharedArrayBuffer ` 的 API 与` ArrayBuffer ` 一模一样,唯一的区别是后者无法共享。
1007
1010
1008
1011
``` javascript
1012
+ // 主线程
1013
+
1009
1014
// 新建 1KB 共享内存
1010
1015
var sharedBuffer = new SharedArrayBuffer (1024 );
1011
1016
1012
- // 主窗口发送数据
1017
+ // 主线程将共享内存的地址发送出去
1013
1018
w .postMessage (sharedBuffer);
1014
1019
1015
- // 本地写入数据
1020
+ // 在共享内存上建立视图,供写入数据
1016
1021
const sharedArray = new Int32Array (sharedBuffer);
1017
1022
```
1018
1023
1019
1024
上面代码中,` postMessage ` 方法的参数是` SharedArrayBuffer ` 对象。
1020
1025
1021
- Worker 进程从事件的 ` data ` 属性上面取到数据。
1026
+ Worker 线程从事件的 ` data ` 属性上面取到数据。
1022
1027
1023
1028
``` javascript
1029
+ // Worker 线程
1024
1030
var sharedBuffer;
1025
1031
onmessage = function (ev ) {
1026
- sharedBuffer = ev .data ; // 1KB 的共享内存,就是主窗口共享出来的那块内存
1032
+ sharedBuffer = ev .data ; // 1KB 的共享内存,就是主线程共享出来的那块内存
1027
1033
};
1028
1034
```
1029
1035
1030
- 共享内存也可以在 Worker 进程创建,发给主进程 。
1036
+ 共享内存也可以在 Worker 线程创建,发给主线程 。
1031
1037
1032
1038
` SharedArrayBuffer ` 与` SharedArray ` 一样,本身是无法读写,必须在上面建立视图,然后通过视图读写。
1033
1039
@@ -1045,13 +1051,14 @@ var primes = new PrimeGenerator();
1045
1051
for ( let i= 0 ; i < ia .length ; i++ )
1046
1052
ia[i] = primes .next ();
1047
1053
1048
- // 向 Worker 进程发送这段共享内存
1054
+ // 向 Worker 线程发送这段共享内存
1049
1055
w .postMessage (ia);
1050
1056
```
1051
1057
1052
- Worker 进程收到数据后的处理如下 。
1058
+ Worker 线程收到数据后的处理如下 。
1053
1059
1054
1060
``` javascript
1061
+ // Worker 线程
1055
1062
var ia;
1056
1063
onmessage = function (ev ) {
1057
1064
ia = ev .data ;
@@ -1060,38 +1067,38 @@ onmessage = function (ev) {
1060
1067
};
1061
1068
```
1062
1069
1063
- 多个进程共享内存,最大的问题就是如何防止两个进程同时修改某个地址 ,或者说,当一个进程修改共享内存以后,必须有一个机制让其他进程同步 。SharedArrayBuffer API 提供` Atomics ` 对象,保证所有共享内存的操作都是“原子性”的,并且可以在所有进程内同步。
1070
+ 多线程共享内存,最大的问题就是如何防止两个线程同时修改某个地址 ,或者说,当一个线程修改共享内存以后,必须有一个机制让其他线程同步 。SharedArrayBuffer API 提供` Atomics ` 对象,保证所有共享内存的操作都是“原子性”的,并且可以在所有进程内同步。
1064
1071
1065
1072
``` javascript
1066
- // 主进程
1073
+ // 主线程
1067
1074
var sab = new SharedArrayBuffer (Int32Array .BYTES_PER_ELEMENT * 100000 );
1068
1075
var ia = new Int32Array (sab);
1069
1076
1070
1077
for (let i = 0 ; i < ia .length ; i++ ) {
1071
1078
ia[i] = primes .next (); // 将质数放入 ia
1072
1079
}
1073
1080
1074
- // worker 进程
1081
+ // worker 线程
1075
1082
ia[112 ]++ ; // 错误
1076
1083
Atomics .add (ia, 112 , 1 ); // 正确
1077
1084
```
1078
1085
1079
- 上面代码中,Worker 进程直接改写共享内存是不正确的 。有两个原因,一是可能发生两个进程同时改写该地址 ,二是改写以后无法同步到其他 Worker 进程 。所以,必须使用` Atomics.add() ` 方法进行改写。
1086
+ 上面代码中,Worker 线程直接改写共享内存是不正确的 。有两个原因,一是可能发生两个线程同时改写该地址 ,二是改写以后无法同步到其他 Worker 线程 。所以,必须使用` Atomics.add() ` 方法进行改写。
1080
1087
1081
1088
下面是另一个例子。
1082
1089
1083
1090
``` javascript
1084
- // 进程一
1091
+ // 线程一
1085
1092
console .log (ia[37 ]); // 163
1086
1093
Atomics .store (ia, 37 , 123456 );
1087
1094
Atomics .wake (ia, 37 , 1 );
1088
1095
1089
- // 进程二
1096
+ // 线程二
1090
1097
Atomics .wait (ia, 37 , 163 );
1091
1098
console .log (ia[37 ]); // 123456
1092
1099
```
1093
1100
1094
- 上面代码中,共享内存` ia ` 的第37号位置,原来的值是` 163 ` 。进程二使用` Atomics.wait() ` 方法,指定只要` ia[37] ` 等于` 163 ` ,就处于“等待”状态。进程一使用` Atomics.store() ` 方法,将` 123456 ` 放入` ia[37] ` ,然后使用` Atomics.wake() ` 方法将监视` ia[37] ` 的一个进程唤醒 。
1101
+ 上面代码中,共享内存` ia ` 的第37号位置,原来的值是` 163 ` 。进程二使用` Atomics.wait() ` 方法,指定只要` ia[37] ` 等于` 163 ` ,就处于“等待”状态。进程一使用` Atomics.store() ` 方法,将` 123456 ` 放入` ia[37] ` ,然后使用` Atomics.wake() ` 方法将监视` ia[37] ` 的一个线程唤醒 。
1095
1102
1096
1103
` Atomics ` 对象有以下方法。
1097
1104
0 commit comments