`postMessage` 或屈服于事件循环或类似的同步共享内存吗?

2024-03-12

我没有看到任何内容JavaScript 规范 https://tc39.github.io/ecma262/, the 提议的 DOM 规范扩展 http://tc39.github.io/ecmascript_sharedmem/dom_shmem.html相关SharedArrayBuffer, 或者当前 WHAT-WG HTML 规范 https://html.spec.whatwg.org/表明当一个线程将消息发送给另一个线程并且另一个线程处理该消息时,共享内存将跨线程同步/更新。 (After一个已经将共享内存发送给另一个。)但是,我也无法通过实验验证它doesn't发生(在我的测试中,我没有看到过时的值)。我是否缺少这样的保证?如果有,保证在哪里?例如,它是否记录在案postMessage我错过了它,或者是否有一些关于返回到保证它的事件循环/作业队列的东西(因为处理来自另一个线程的消息涉及这样做)等等?或者,这绝对是not有保证(并且该信息在某个规范中)?

Please不要推测或做出“合理的猜测”。我正在寻找硬信息:来自规范来源的引用,一个可复制的实验,表明它不能得到保证(尽管我想这是否只是一个实现错误的问题),诸如此类的事情。


以下是我的测试的来源,尚未能够捕获不同步的内存。要运行它,您需要使用当前支持的浏览器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>

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}`);

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;
        }
    }
});

使用该代码,如果内存未同步,我希望主线程在某个时刻看到共享数组中的陈旧值。但在我看来,这段代码完全有可能只是happens由于消息传递涉及的时间尺度相对较大......


TL;DR:是的,确实如此。


In es-discuss 上的主题 https://esdiscuss.org/topic/shared-memory-sync-update-question共享内存提案的作者 Lars Hansen 写道:

在浏览器中,postMessage 发送和接收始终旨在以与写入-读取对相同的方式创建同步边缘。http://tc39.github.io/ecmascript_sharedmem/shmem.html#WebBrowserEmbedding http://tc39.github.io/ecmascript_sharedmem/shmem.html#WebBrowserEmbedding

当规范转移到 es262 文档时,不确定这篇散文最终去了哪里。

我接着说:

Thanks!

看起来至少部分在这里:https://tc39.github.io/ecma262/#sec-host-synchronizes-with https://tc39.github.io/ecma262/#sec-host-synchronizes-with

所以,一个问题(好吧,两个问题)只是针对我们这些不熟悉的人 在内存模型部分的术语中。鉴于:

  1. 线程 A 通过以下方式向线程 B 发送 1k 共享块postMessage
  2. 线程 B 直接写入该块中的各个位置(而不是通过Atomics.store)
  3. 线程 B 执行以下操作postMessage到线程A(不引用块 在里面postMessage)
  4. 线程A接收消息并从块中读取数据(不通过Atomics.load)

...我是否更正,在步骤 4 中保证线程 Awill可靠地看到步骤 2 中线程 B 对该块的写入,因为postMessage是一个“同步边缘”确保(除其他外) CPU L1d 缓存是否是最新的,等等?

同样,如果(!)我读得正确,在你的 Mandlebrot 例子中,你 有一个Atomics.wait在共享块中的单个位置上,并且当 线程醒来时它似乎假设块中的其他数据(而不是在wait范围)可以可靠地直接读取。这也是“同步” 边缘”?

他对此回复道:

...确保(除其他外) CPU L1d 缓存是否是最新的,等等?

是的,这就是该语言的意图。对内存的写入应该发生在 postMessage 之前,并且接收消息应该发生在读取之前。

……那也是“同步边缘”?

是的,同样的论点。写入发生在唤醒之前,等待的唤醒发生在读取之前。

所有这些都是有意为之,以便允许通过廉价的非同步写入和读取来写入和读取数据,然后进行(相对昂贵的)同步以确保正确的可观察性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

`postMessage` 或屈服于事件循环或类似的同步共享内存吗? 的相关文章

随机推荐