Javascript “postMessage”或“屈服于事件循环”或类似的同步共享内存吗?
我在中没有看到任何内容,与Javascript “postMessage”或“屈服于事件循环”或类似的同步共享内存吗?,javascript,shared-memory,sharedarraybuffer,Javascript,Shared Memory,Sharedarraybuffer,我在中没有看到任何内容,与SharedArrayBuffer相关的,或者当一个线程向另一个线程发布消息,而另一个线程处理消息时,建议跨线程同步/更新共享内存。(在一个已经将共享内存发送给另一个之后)但是,我也无法通过实验验证它是否没有发生(在我的测试中,我没有看到过时的值)。我是否缺少这样的保证?如果是,保证在哪里?例如,它是为postMessage而记录的,而我却错过了它,还是有什么关于退回到事件循环/作业队列以保证它的存在(因为处理来自另一个线程的消息需要这样做),等等。?或者,它肯定是而不
SharedArrayBuffer
相关的,或者当一个线程向另一个线程发布消息,而另一个线程处理消息时,建议跨线程同步/更新共享内存。(在一个已经将共享内存发送给另一个之后)但是,我也无法通过实验验证它是否没有发生(在我的测试中,我没有看到过时的值)。我是否缺少这样的保证?如果是,保证在哪里?例如,它是为postMessage
而记录的,而我却错过了它,还是有什么关于退回到事件循环/作业队列以保证它的存在(因为处理来自另一个线程的消息需要这样做),等等。?或者,它肯定是而不是保证的(并且该信息在某个地方的规范中)
请不要猜测或做出“合理的猜测”。我在寻找硬信息:来自规范来源的引用,一个可复制的实验,表明它不能保证(尽管我想这是一个是否只是实现错误的问题),诸如此类
下面是我的测试的源代码,这些测试还不能捕获非同步内存。要运行它,您需要使用当前支持
SharedArrayBuffer
的浏览器,我认为目前这意味着Chromev67或更高版本(Firefox、Edge和Safari都支持,但在2018年1月因Spectre和Meldown而禁用;Chrome也支持,但在v67中重新启用了它[2018年7月]在启用站点隔离功能的平台上)
同步测试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>
sync test postMessage worker.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}`);
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;
}
}
});
使用该代码,如果内存没有同步,我希望在某个时候主线程会在共享数组中看到一个过时的值。但是(在我看来)这段代码很可能只是因为消息传递涉及到相对较大的时间尺度才起作用…TL;医生:是的,的确如此。
年,共享内存提案的作者Lars Hansen写道: 在浏览器中,postMessage发送和接收始终旨在以与写-读对相同的方式创建同步边缘 不确定当规范被转移到es262文档时,这篇文章的结尾是什么 我接着说: 谢谢 看起来至少部分是这样的: 所以,一个问题(好吧,两个问题)只针对我们这些不太精通的人 在“内存模型”部分的术语中。鉴于:
postMessage
Atomics.store
)postMessage
(不引用块
在postMessage
)中Atomics.load
)postMessage
是确保(除其他外)同步的“边缘”
CPU L1d缓存是最新的,等等
类似地,如果(!)我读对了,在你的Mandlebrot例子中,你
拥有一个原子。在共享块中的单个位置上等待,当
线程唤醒时,它似乎假定块中有其他数据(而不是
wait
range)可以可靠地直接读取。这也是一种“同步”
边缘“
他回答说:
…确保(除其他事项外)
CPU L1d缓存是最新的,等等
是的,这就是那种语言的意图。对内存的写入应该发生在postMessage之前,而接收消息应该发生在读取之前
。。。这也是“同步优势”
是的,同样的论点。写入发生在唤醒之前,等待的唤醒发生在读取之前
所有这些都是有意的,以便允许使用廉价的非同步写入和读取来写入和读取数据,然后进行(相对昂贵的)同步以确保适当的可观测性
规范说事件序列没有未定义的行为,所以任何特定的情况都必须存在。您的问题是如何找到
postMessage
在所描述的案例中的合适位置。@MinusFour-公平点,希望我能在那里得到一些帮助。:-)它还说“我们建议程序保持无数据竞争,即使其不可能在同一内存位置上同时进行非原子操作。对于无数据竞争的程序,无需了解内存模型的细节。这些细节不太可能建立直觉,帮助人们更好地编写ECMAScript。“因此,我戴着务实而非学术的帽子,也许我应该在这种情况下使用原子学。但我的学术负责人很好奇……事实上,我甚至不确定Atomics
是否能帮到你。我对该规范的理解是,如果两个事件的内存范围重叠,则即使是由Atomics
生成的事件也会产生错误。除非我在Atomics
的规范中遗漏了一些东西,在那里它可以处理事件,这样它们就永远不会在同一个队列中。