Javascript Workers-为什么最近处理了worker消息,我可以对它采取措施吗



我有一个Worker,它与"主线程";。为了正确工作,我必须确保工作线程在主线程访问SAB之前能够访问它。(EDIT:创建工作线程的代码必须在一个单独的函数中(EDIT2:返回一个指向SAB的数组)。)(也许,你会告诉我,这已经不可能了)。

初始代码如下:

function init() {
var code = `onmessage = function(event) {
console.log('starting');
var buffer=event.data;
var arr = new Uint32Array(buffer);// I need to have this done before accessing the buffer again from the main
//some other code, manipulating the array
}`
var buffer = new SharedArrayBuffer(BUFFER_ELEMENT_SIZE);
var blob = new Blob([code], { "type": 'application/javascript' });
var url = window.URL || window.webkitURL;
var blobUrl = url.createObjectURL(blob);
var counter = new Worker(blobUrl);
counter.postMessage(buffer);
let res = new Uint32Array(buffer);
return res;
}
function test (){
let array = init();
console.log('main');
//accessing the SAB again
};

工作程序代码总是在test()之后执行,控制台总是显示main,然后显示starting

使用超时没有帮助。考虑test的以下代码:

function test (){
let array = [];
console.log('main'); 
setTimeout(function(){
array = initSAB();
},0);
setTimeout(function(){
console.log('main');
//accessing the SAB again
},0);
console.log('end');
};

控制台首先显示end,然后显示main,最后显示starting

然而,将缓冲区分配给test()函数之外的全局数组可以完成任务,即使没有超时。

我的问题如下:

  • 为什么工作程序在发送(=接收)消息后不直接启动。AFAIK,工作人员有自己的事件队列,所以他们不应该依赖于主堆栈变空
  • 是否有详细说明员工发送消息后开始工作的时间的规范
  • 在不使用全局变量的情况下,是否有方法确保工人在再次访问SAB之前已经启动?(一个人可能需要忙着等待,但我要小心…)可能没有办法,但我想确定

编辑

更准确地说:

  • 在完全并行运行的场景中,Worker将能够邮件发布后立即处理。这显然事实并非如此
  • 大多数浏览器API(Worker就是这样一个API)使用回调队列来处理对API的调用。但如果这适用,信息将是在执行超时回调之前发布/处理
  • 更进一步:如果我试图在postMessage之后忙于等待,从SAB读取,直到它更改一个值将阻止无限编程。对我来说,这意味着浏览器not发布消息,直到调用堆栈为空直到我知道,这种行为没有记录在案,我无法解释

总结:如果postMessage的调用在函数内,我想知道浏览器如何确定何时发布消息以及由工作人员处理消息我已经找到了一个变通方法(全局变量),所以我更感兴趣的是它在幕后是如何工作的。但如果有人能给我看一个工作的例子,我会接受的

编辑2:

使用全局变量的代码(运行良好的代码)看起来像这个

function init() {
//Unchanged
}
var array = init(); //global
function test (){
console.log('main');
//accessing the SAB again
};

它打印starting,然后打印main到控制台。

同样值得注意的是:如果我用Firefox浏览器调试代码(Chrome未测试)我会在没有全局变量的情况下得到我想要的结果

为什么在消息被发送[t](=接收到?)之后工作程序不直接启动。AFAIK,工作人员有自己的事件队列,所以他们不应该依赖于主堆栈变空

首先,即使Worker对象在主线程中同步可用,但在实际的Worker线程中,在能够处理消息之前,还有很多事情要做:

  • 它必须执行一个网络请求来检索脚本内容。即使使用blobURI,它也是一个异步操作
  • 它必须初始化整个js上下文,所以即使网络请求非常快,这也会增加并行执行时间
  • 它必须等待主脚本执行之后的事件循环帧来处理您的消息。即使初始化非常快,它也要等待一段时间

因此,在正常情况下,Worker在您需要数据时执行代码的可能性很小。

现在您谈到了阻塞主线程。

如果我试图在postMessage之后忙于等待,从SAB读取消息,直到它改变一个值,将无限地阻止程序

在Worker初始化期间,消息暂时保留在主线程上,即所谓的外部端口中。只有在获取脚本之后,这个外部端口才会与内部端口此,如果在端口被纠缠之前阻塞主线程,它将无法将其传递给工作线程。

是否有详细说明工作人员在发送消息后何时开始工作的规范

当然,更具体地说,端口消息队列在步骤26启用,事件循环实际上在步骤29启动。

在不使用全局变量的情况下再次访问SAB之前,是否有方法确保工人已经启动?[…]

当然,让Worker在发布消息时向主线程发布消息。

// some precautions because all browsers still haven't reenabled SharedArrayBuffers
const has_shared_array_buffer = window.SharedArrayBuffer;
function init() {
// since our worker will do only a single operation
// we can Promisify it
// if we were to use it for more than a single task, 
// we could promisify each task by using a MessagePort
return new Promise((resolve, reject) => {
const code = `
onmessage = function(event) {
console.log('hi');
var buffer= event.data;
var arr = new Uint32Array(buffer);
arr.fill(255);
if(self.SharedArrayBuffer) {
postMessage("done");
}
else {
postMessage(buffer, [buffer]);
}
}`
let buffer = has_shared_array_buffer ? new SharedArrayBuffer(16) : new ArrayBuffer(16);
const blob = new Blob([code], { "type": 'application/javascript' });
const blobUrl = URL.createObjectURL(blob);
const counter = new Worker(blobUrl);
counter.onmessage = e => {
if(!has_shared_array_buffer) {
buffer = e.data;
}
const res = new Uint32Array(buffer);
resolve(res);
};
counter.onerror = reject;
if(has_shared_array_buffer) {
counter.postMessage(buffer);
}
else {
counter.postMessage(buffer, [buffer]);
}
});
};
async function test (){
let array = await init();
//accessing the SAB again
console.log(array);
};
test().catch(console.error);

根据MDN:

在主页和辅助进程之间传递的数据是复制的,而不是共享的。对象在交给工作者时进行序列化,然后在另一端进行反序列化。页面和辅助进程不共享同一个实例,因此最终结果是在每一端创建一个重复实例。大多数浏览器将此功能实现为结构化克隆。

阅读有关向工人传输数据和从工人传送数据的更多信息

下面是一个与工作者共享缓冲区的基本代码。它创建一个具有偶数值(i*2)的数组,并将其发送给worker。它使用原子操作来更改缓冲区值。

为了确保工作人员已经启动,您可以使用不同的消息。

var code = document.querySelector('[type="javascript/worker"]').textContent;
var blob = new Blob([code], { "type": 'application/javascript' });
var blobUrl = URL.createObjectURL(blob);
var counter = new Worker(blobUrl);
var sab;
var initBuffer = function (msg) {
sab = new SharedArrayBuffer(16);
counter.postMessage({
init: true, 
msg: msg, 
buffer: sab
});
};
var editArray = function () {
var res = new Int32Array(sab);
for (let i = 0; i < 4; i++) {
Atomics.store(res, i, i*2);
}
console.log('Array edited', res);
};
initBuffer('Init buffer and start worker');
counter.onmessage = function(event) {
console.log(event.data.msg);
if (event.data.edit) {
editArray();
// share new buffer with worker
counter.postMessage({buffer: sab});
// end worker
counter.postMessage({end: true});
}
};
<script type="javascript/worker">
var sab;
self['onmessage'] = function(event) {
if (event.data.init) {
postMessage({msg: event.data.msg, edit: true});
}
if (event.data.buffer) {
sab = event.data.buffer;
var sharedArray = new Int32Array(sab);
postMessage({msg: 'Shared Array: '+sharedArray});
}
if (event.data.end) {
postMessage({msg: 'Time to rest'});
}
};
</script>

相关内容

最新更新