[ECMAScript] Shared Memory

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.htmlindex.jsworker.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还支持wakewait两种操作,类似于互斥锁,
写操作:
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

你可能感兴趣的:([ECMAScript] Shared Memory)