T.J*_*der 11 javascript shared-memory sharedarraybuffer
我没有在JavaScript规范中看到任何内容,建议的DOM规范扩展相关SharedArrayBuffer,或者当前的WHAT-WG HTML规范建议当一个线程将消息发布到另一个线程和另一个线程时,将跨线程同步/更新共享内存处理消息.(在已经将共享内存发送给另一个之后.)但是,我也无法通过实验验证它没有发生(在我的测试中,我没有看到陈旧的值).是否有一些这样的保证,我错过了,如果是这样,它在哪里得到保证?例如,它是否记录在案postMessage而且我错过了它,或者有什么东西可以回到事件循环/作业队列来保证它(因为处理来自另一个线程的消息涉及这样做)等等?或者,它绝对不能保证(并且信息在某个地方的规范中)?
请不要推测或做出"合理猜测".我正在寻找难以获得的信息:来自规范来源的引文,一个可复制的实验,表明它不能得到保证(尽管我认为这是一个问题,它是否仅仅是一个实现错误),那样的事情.
下面是我的测试的源,但尚未能够捕获不同步的内存.要运行它,您需要使用当前支持的浏览器,SharedArrayBuffer我认为此刻意味着Chrome v67或更高版本(Firefox,Edge和Safari都有支持,但在2018年1月禁用它以响应Spectre和Meltdown Chrome也做了,但是在启用了网站隔离功能的平台上的v67 [2018年7月]重新启用了它.
sync-test-postMessage.html:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Sync Test postMessage</title>
</head>
<body>
<script src="sync-test-postMessage-main.js"></script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
sync-test-postMessage-main.js:
const array = new Uint32Array(new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT));
const worker = new Worker("./sync-test-postMessage-worker.js");
let counter = 0;
const limit = 1000000;
const report = Math.floor(limit / 10);
let mismatches = 0;
const now = performance.now();
const log = msg => {
console.log(`${msg} - ${mismatches} mismatch(es) - ${performance.now() - now}ms`);
};
worker.addEventListener("message", e => {
if (e.data && e.data.type === "ping") {
++counter;
const value = array[0];
if (counter !== value) {
++mismatches;
console.log(`Out of sync! ${counter} !== ${value}`);
}
if (counter % report === 0) {
log(`${counter} of ${limit}`);
}
if (counter < limit) {
worker.postMessage({type: "pong"});
} else {
console.log("done");
}
}
});
worker.postMessage({type: "init", array});
console.log(`running to ${limit}`);
Run Code Online (Sandbox Code Playgroud)
sync-test-postMessage-worker.js:
let array;
this.addEventListener("message", e => {
if (e.data) {
switch (e.data.type) {
case "init":
array = e.data.array;
// fall through to "pong"
case "pong":
++array[0];
this.postMessage({type: "ping"});
break;
}
}
});
Run Code Online (Sandbox Code Playgroud)
使用该代码,如果内存未同步,我希望在某些时候主线程可以在共享数组中看到过时的值.但它是完全有可能(在我看来),此代码只发生,因为涉及的消息传递的相对大的时间尺度的工作...
TL;DR:是的,确实如此。
在es-discuss 的帖子中,共享内存提案的作者 Lars Hansen 写道:
在浏览器中,postMessage 发送和接收始终旨在以与写入-读取对相同的方式创建同步边缘。http://tc39.github.io/ecmascript_sharedmem/shmem.html#WebBrowserEmbedding
当规范转移到 es262 文档时,不确定这篇散文最终去了哪里。
我接着说:
谢谢!
看起来至少部分在这里: https: //tc39.github.io/ecma262/#sec-host-synchronizes-with
因此,对于我们这些不熟悉内存模型部分术语的人来说,有一个问题(嗯,两个问题)。鉴于:
- 线程 A 通过以下方式向线程 B 发送 1k 共享块
postMessage- 线程 B 直接写入该块中的各个位置(不通过
Atomics.store)- 线程 B
postMessage对线程 A 执行 a (不引用 中的块postMessage)- 线程A接收消息并从块中读取数据(不通过
Atomics.load)...我是否正确,在步骤 4 中,保证线程 A能够 可靠地看到步骤 2 中线程 B 对该块的写入,因为这
postMessage是一个“同步边缘”,确保(除其他外)CPU L1d 缓存已启动- 迄今为止,等等?类似地,如果(!)我正确地阅读它,在您的 Mandlebrot 示例中,您
Atomics.wait在共享块中的单个位置上有一个,并且当线程唤醒时,它似乎假定该块中的其他数据(不在范围内wait) )可以可靠地直接读取。这也是“同步边缘”?
他对此回复道:
...确保(除其他外)CPU L1d 缓存是最新的,等等?
是的,这就是该语言的意图。对内存的写入应该发生在 postMessage 之前,并且接收消息应该发生在读取之前。
……那也是“同步边缘”?
是的,同样的论点。写入发生在唤醒之前,等待的唤醒发生在读取之前。
所有这些都是有意为之,以便允许通过廉价的非同步写入和读取来写入和读取数据,然后进行(相对昂贵的)同步以确保正确的可观察性。