首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将SharedArrayBuffer同步到主线程

将SharedArrayBuffer同步到主线程
EN

Stack Overflow用户
提问于 2017-08-01 13:32:18
回答 2查看 2K关注 0票数 3

我在将SharedArrayBuffer同步到主线程时遇到了问题。

下面是一个场景:

我有两名员工负责我的程序的不同方面。第一个工作人员负责对象交互,第二个工作人员负责计算可见性等,主线程将进行可视化。

首先,第一个工作人员创建一个具有以下布局的SharedArrayBuffer

代码语言:javascript
复制
new SharedArrayBuffer(112);
[   
    Lock:      4 Byte
    MetaInfo:  4 Byte
    Location: 12 Byte
    Scale:    12 Byte
    Rotation: 16 Byte
    Matrix:   64 Byte
]

然后,他将SAB发送给主线程和第二个工作人员,并将位置、比例和旋转属性存储在缓冲区中。每次更新字段时,他都会锁定SAB,更新值,并将MetaInfo字段(transform标志)的第一个位设置为true。

如果设置了转换标志,第二个工作人员将从给定的位置、尺度和旋转字段计算矩阵,并将其保存在矩阵字段中。之后,MetaInfo字段的第二个位(矩阵标记)将被设置为true。

如果设置了矩阵标志,则主线程现在需要读取最终矩阵。

问题来了:在工作人员上,可以使用lock字段上的Atomics.wait方法锁定缓冲区。但是主线程缺乏这样的特性,导致口吃和“跳跃”。是否有一致的方法来阻止其他工人在阅读过程中写入SAB?

下面是我的SharedArrayBuffer包装器的代码:

代码语言:javascript
复制
class SharedObject {
    SharedBuffer: SharedArrayBuffer; // the shared array buffer
    Lock: Int32Array;  // view for lockíng the buffer
    MetaInfo: Int32Array; // view for meta info
    Location: Float32Array;

    constructor(buffer) {
        // if valid buffer is passed assign it to this object
        if (buffer !== undefined && buffer instanceof SharedArrayBuffer && buffer.byteLength == 112) {
            this.SharedBuffer = buffer;
        } else {
            // create new shared array buffer
            this.SharedBuffer = new SharedArrayBuffer(112);
        }

        this.Lock = new Int32Array(this.SharedBuffer, 0, 4);
        this.MetaInfo = new Int32Array(this.SharedBuffer, 4, 8);

        [ ... init the rest of the views ... ]

        // init the lock element
        if (buffer === undefined) {
            Atomics.store(this.Lock, 0, 1);
        }

    }

    lock() {
        Atomics.wait(this.Lock, 0, 0);
        Atomics.store(this.Lock, 0, 0);
        return true;
    }

    free() {
        if (Atomics.wake(this.Lock, 0, 1) == 0) {
            Atomics.store(this.Lock, 0, 1);
        }
        return true;
    }

    setFlag(flag) {
        this.MetaInfo[0] = this.MetaInfo[0] | flag;
    }
    isFlagSet(flag) {
        return (this.MetaInfo[0] & flag) > 0;
    }
    resetFlag(flag) {
        this.MetaInfo[0] = this.MetaInfo[0] - (this.MetaInfo[0] & flag);
    }
}

注意,锁和空闲方法不能在主线程中使用,因为:

注意:此操作仅适用于共享的Int32Array,不允许在主线程上运行。

物体/原子/等待

这种设置是否可能在一个SharedArrayBuffer中有多个独立的字段,或者是否应该考虑对每个应用程序使用多个SharedArrayBuffer

EN

回答 2

Stack Overflow用户

发布于 2017-08-01 14:59:42

经过研究,防止主线程使用Atomics.wait()的选择似乎是避免同步线程阻塞,因为主线程处理用户事件和页面呈现以及其他服务,并且允许Atomics.wait()会导致web应用程序上的用户体验差。

API接口之后是API接口,它目前还没有在Chrome上实现,但可以在火狐上使用。

使用屏幕外的画布,您可以从用于呈现的web工作者中提取Atomics.wait(),在从共享数组缓冲区读取数据之后应用gl操作,然后调用gl.commit(),这将将gl框架呈现给主线程。

不幸的是,由于火狐是目前唯一支持OffscreenCanvas API的浏览器,而NW.js只支持Chrome,这个特殊的同步挑战似乎无法克服,因为在Chrome上的同一线程中缺乏对Atomics.wait()和WebGL的支持。

票数 3
EN

Stack Overflow用户

发布于 2019-02-22 12:49:34

不要认为这个问题仍然相关。然而,如果有人面临同样的问题,以下是我的想法:

  1. 不要自己实现锁机制,使用像https://github.com/lars-t-hansen/js-lock-and-condition/blob/master/lock.js这样的库 下面是锁函数中的一个问题:
代码语言:javascript
复制
        Atomics.wait(this.Lock, 0, 0); // <-- 2 threads can check 'lock' flag one by one 
                                       //     and pass to the next line
        Atomics.store(this.Lock, 0, 0); // <-- then they both set 0 as a 'lock' flag
                                        //     and move further
        return true;
    }

所以,你会有比赛的条件。

另外,请查看我为测试SharedArrayBuffer https://github.com/vrudkovskiy/textediting-test-js/blob/master/packages/client/src/utils/SharingLock.ts而编写的这个演示应用程序。

更新。

Atomics类仅在单个缓冲区元素上提供原子操作。但是,正如我所理解的,您需要锁定对整个缓冲区的访问,同时一些线程从/对其进行读/写。

因此,例如,如果有2个线程将数据写入内存的同一部分,则可能出现以下情况:

线程1写: 11111111

线程2写: 22222222

结果可以是: 11221122 -这将是一些随机的结果。

而且,不幸的是,在Atomics中没有原子信号量,应该使用Atomics.compareExchange和Atomics.wait调用的组合来实现它。这真的不容易,因为有很多情况你应该涵盖,这就是为什么我说不做自己。你可以找到更好的解释这里

  1. 在此问题中,不需要阻塞主线程,而是主线程应该阻止其他线程在主线程读取共享内存时访问共享内存。TryLock函数可以使用https://github.com/lars-t-hansen/js-lock-and-condition/blob/master/lock.js#L126

在本例中,主线程多次尝试锁定共享内存,比方说,每个帧(呈现都不是冻结的),一旦成功,它就会呈现数据。

更新。

主线程不访问数据,它只能尝试再次阻塞数据。当然会有性能上的缺陷。

  1. 另一种技术是不使用Atomics,而是发布消息。例如:
代码语言:javascript
复制
- worker calculated some data
- it sends a notification to main thread about it
- worker does not use that part of memory till main thread allows it by notifying back through postMessage function

  1. 最后一点。上述锁库的作者向异步锁定https://github.com/tc39/proposal-atomics-wait-async标准提交了一份建议--它包含一个使用第三个工作器来锁定主线程数据的多填充,换句话说,主线程委托将资源锁定给单独的工作人员,后者通知父线程成功锁定的情况。多填充包含一个明显的性能缺陷,但是本机实现应该更快。

更新。

您可以尝试将主线程中的tryLock与工作线程中的框架绘图同步结合起来(以某种方式预测主线程何时绘制数据,并在那时不碰它),听起来像是一种解决办法,我不确定是否可能:)

或者,完全不阻止对缓冲区的访问是可以的(只需绘制像11221122这样的结果),所以只需使用Atomics来读取/更改单个元素。或者锁定对缓冲区的一些小部分的访问。

在任何情况下,没有理想的多线程解决方案,您总是在性能和数据一致性之间进行选择。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45439334

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档