1. 背景
Shared Memory曾经是TC39的提案,目前已经成为了最新ES 2017规范的一部分。
与Shared Memory相关的章节有:
6.2.7 Data Blocks
8.7 Agent
8.8 Agent Clusters
22.2 TypedArray Objects
24.1ArrayBuffer Objects
24.2 SharedArrayBuffer Objects
24.3 DataView Objects
24.4 The Atomics Object
27 Memory Model
2. 命令行启动Chrome
Chrome 59.0.3071.115 中,默认关闭了SharedArrayBuffer特性,我们需要用命令行启动Chrome,
sudo "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --js-flags=--harmony-sharedarraybuffer --enable-blink-feature=SharedArrayBuffer
3. Web Workers
(1)全局安装安装httpserver命令行工具,
npm install -g httpserver
(2)在工作目录下,启动web server
cd ~/Test
httpserver
(3)新建三个文件,index.html
,index.js
和worker.js
,
Document
// index.js
const worker = new Worker('./worker.js');
worker.postMessage(1);
worker.onmessage = e => {
console.log(`main receive message: ${e.data}`); //2
};
// worker.js
onmessage = e => {
console.log(`worker receive message: ${e.data}`); //1
postMessage(2);
};
(4)在浏览器中访问index.html
http://localhost:8080/index.html
注:
(1)worker中不能再使用new Worker
(2)worker中使用全局变量onmessage
接收消息,使用全局变量postMessage
回传消息。
(3)postMessage
可以传入多种类型的参数,不一定是字符串。
(4)onmessage
使用e.data
,而不是e
,来接收postMessage
传入的对象。
4. SharedArrayBuffer
使用postMessage
发送SharedArrayBuffer
对象,
// index.js
const worker = new Worker('./worker.js');
const buffer = new SharedArrayBuffer(1024);
worker.postMessage(buffer);
// worker.js
onmessage = ({ data }) => {
console.log(data); // SharedArrayBuffer {}
};
5. 创建buffer的view
和ArrayBuffer一样,我们需要创建view来访问buffer。
例如:创建一个Int32Array
view,
var buffer = new ArrayBuffer(8);
var view = new Int32Array(buffer);
postMessage
可以直接发送一个view,
// index.js
const worker = new Worker('./worker.js');
const buffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 10);
var ia = new Int32Array(buffer);
worker.postMessage(ia);
// worker.js
onmessage = ({ data }) => {
console.log(data); //Int32Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
};
6. Atomics
由于SharedArrayBuffer是共享的,一个写操作会对所有共享该内存的worker可见,
但是,一个写操作需要多久才能同步到其他worker中,是不确定的。
ES规范中,提供了Atomics来保证读写操作的原子性。
写操作:把100
保存在ia
索引为0
的位置,
Atomics.store(ia, 0, 100);
读操作:读取ia
索引为0
位置的值,
while (Atomics.load(ia, 0) == 0){};
console.log(ia[0]);
这里使用了Atomics.load
,类似自旋锁,
如果ia
的值一直没有改变,就会无限循环等待下去,极大的浪费CPU时间。
Atomics还支持wake
和wait
两种操作,类似于互斥锁,
写操作:
Atomics.wake
在给定索引位置0
处,唤起一个在wait queue中睡眠的worker
Atomics.store(ia, 0, 100);
Atomics.wake(ia, 0, 1);
读操作:
Atomics.wake
验证ia
索引位置0
保存的值是否还是0
,如果是的话就继续睡眠,
Atomics.wait(ia, 0, 0);
console.log(ia[0]);
参考
Shared memory - a brief tutorial
Github: tc39/ecmascript_sharedmen
ECMAScript 2017 Language Specification
MDN: Web Workers API
MDN: SharedArrayBuffer
HTML Living Standard: web workers