代码之家  ›  专栏  ›  技术社区  ›  T.J. Crowder

“postMessage”或“yielding to the event loop or similar sync”是否共享内存?

  •  11
  • T.J. Crowder  · 技术社区  · 6 年前

    我什么也没看到 the JavaScript spec ,的 proposed DOM spec extensions SharedArrayBuffer current WHAT-WG HTML spec 当一个线程向另一个线程发布消息而另一个线程处理消息时,建议跨线程同步/更新共享内存( 之后 一个已经将共享内存发送给另一个了。)但是,我也无法通过实验验证它 发生(在我的测试中,我没有看到过时的值)。有没有这样的保证,我失踪了,如果是的话,在哪里保证?例如,是否记录了 postMessage 我已经错过了它,或者是否有什么关于退回到事件循环/作业队列来保证它(因为处理来自另一个线程的消息需要这样做),等等。?或者说,这是肯定的吗 保证(信息在某个地方的规范中)?

    拜托 不要猜测或做出“合理的猜测”。我在寻找硬信息:来自规范来源的引文,一个可复制的实验,它表明这是不能保证的(尽管我认为这是一个是否只是实现错误的问题),诸如此类的东西。


    下面是我的测试的源代码,这些测试还不能捕获不同步的内存。要运行它,您需要使用当前支持的浏览器 ,我认为目前这意味着Chromev67或更高版本(Firefox、Edge和Safari都支持Chromev67,但在2018年1月因幽灵和熔毁而禁用了Chromev67;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;
            }
        }
    });
    

    使用该代码,如果内存没有同步,我希望在某个时候主线程在共享数组中看到一个过时的值。但很可能(在我看来)这段代码 发生 因为消息传递涉及的时间尺度相对较大。。。

    1 回复  |  直到 6 年前
        1
  •  2
  •   T.J. Crowder    6 年前

    热释光;医生:是的。


    the thread on es-discuss

    在浏览器中,postmessagesend和receive总是以写-读对相同的方式创建同步边。 http://tc39.github.io/ecmascript_sharedmem/shmem.html#WebBrowserEmbedding

    当规范被转移到es262文档时,不确定这篇文章的结尾。

    我接着说:

    谢谢!

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

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

    1. 线程A通过发送一个1k共享块给线程B postMessage
    2. 线程B直接(而不是通过)写入该块中的各个位置 Atomics.store )
    3. 邮递 邮递 )
    4. Atomics.load )

    …在第4步中保证线程A 从步骤2中可靠地看到线程B对该块的写入,因为 邮递 CPU L1d缓存是最新的,等等。?

    同样,如果(!)我读对了,在你的曼德勒腐烂的例子里,你 有一个 Atomics.wait 在共享块中的单个位置上 线程唤醒时,似乎假定块中有其他数据(而不是 wait 范围)可以可靠地直接读取。这也是“同步” “边缘”?

    他回答说:

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

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

    ... 这也是“同步优势”?

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