6

虽然我知道 JavaScript 本质上是单线程的,并且通常不赞成此类事情,但我想知道是否有任何方法可以让 WebWorker 等待主线程提供一些数据,而不会破坏 WebWorker 的调用堆栈。

由于这是一个有趣的项目,我可以使用无法在旧浏览器上可靠运行的新技术和东西,只要它们有效,我不介意深奥的黑客攻击。

我考虑过的其他一些解决方案:

  • 在循环中不断轮询LocalStorage,直到在预定的key处有数据。这似乎可行,因为即使在循环中轮询时,其他线程的 LocalStorage 更新也应该对当前线程可见,从所有关于 LocalStorage 的线程安全性的讨论以及多个选项卡写入同一个 LocalStorage 键的情况来看。这种方法的缺点是它不是真正的“等待”,即工作线程仍然在 LocalStorage 上消耗全部 CPU 使用率。虽然 LocalStorage 通常使用锁来实现,但不可能长时间持有 LocalStorage 锁(锁被释放一次getItemsetItem返回)。

  • ECMA 脚本 6 yield。这在这里不起作用,因为它要求调用堆栈中的所有函数(直到您想要屈服的地方)都被标记为生成器函数。我想暂停我的 WebWorker 的地方有一个调用堆栈,其中包含 WebAssembly 函数,不能将其标记为生成器函数。

  • 索引数据库。这不起作用,因为 IndexedDB 不支持同步请求。

我知道这个类似的问题,但是这个问题专门讨论了这个onmessage事件,并且在 2012 年被问到,在yieldWebAssembly 被引入之前。

有没有办法以某种方式模拟 WebWorker 线程上的锁定,或者以其他方式,以便它会等到一些数据可用?

4

1 回答 1

9

编辑请注意,SharedArrayBuffer所有主要浏览器(2018 年 1 月 5 日)中默认禁用该功能以响应Spectre,并且尚未完全重新启用


JavaScript 的SharedArrayBuffer听起来非常适合你:

  • 新技术:刚刚在 1 月的 TC39 会议上进入第 4 阶段(赶上 ES2017)
  • 不能在旧浏览器上可靠地运行(旧版本有不同的 API,或者没有可用的实现)
  • 深奥的破解(类似于 C++ 内存模型
  • 作品

出于您的目的,您希望让 WebWorker 等到数据可用。SharedArrayBuffer您可以使用自旋循环(直到Atomics.load值更改),但最好Atomics.wait在其他工作人员向您发送Atomics.notify. 这个后来的 API 很大程度上受到了Linux 的 futex 的启发,如果你等待的值不可用,它不会不必要地旋转。

// Main program:
var w = new Worker("worker.js")
var sab = new SharedArrayBuffer(1024);
w.postMessage(sab);
var i = new Int32Array(sab);
// Maybe wait for worker.js to message back that it's ready through onmessage?
//
// Fill i with some data!
// ...
//
// Notify one worker, at location 0, with value 1.
Atomics.store(i, 0, 1);
Atomics.notify(i, 0, /* notify count */ 1);


// worker.js:
var sab;
var i;
onmessage = function (ev) {
    sab = ev.data;
    var i = new Int32Array(sab);
}
// Maybe tell the main program we're ready through postMessage?
//
// ...
// Wait until location 0 isn't value 0
Atomics.wait(i, 0, 0);

记住:阻塞主线程是个坏主意!如果您这样做,您的网站将无响应。您的问题是关于阻止工作人员,但读者可能有兴趣从主线程等待。不!

一个非常相似且兼容的 API最终将在 WebAssembly 中可用这是一份早期的提案草案。当我说兼容时:我们希望 WebAssembly 能够使用与SharedArrayBufferJavaScript 相同的功能,并且两者都能够通过它进行无缝通信。

于 2017-03-27T09:09:35.373 回答