Javascript工作者-为什么工作者消息被处理得如此之晚,我能做些什么来反对它吗?
我有一个与“主线程”共享SharedArrayBuffer的工作线程。为了正确工作,我必须确保工作线程在主线程访问SAB之前能够访问它。(编辑:创建辅助进程的代码必须在一个单独的函数中(编辑2:返回一个指向SAB的数组)。(也许,这已经不可能了,你会告诉我) 初始代码如下所示:Javascript工作者-为什么工作者消息被处理得如此之晚,我能做些什么来反对它吗?,javascript,web-worker,sharedarraybuffer,Javascript,Web Worker,Sharedarraybuffer,我有一个与“主线程”共享SharedArrayBuffer的工作线程。为了正确工作,我必须确保工作线程在主线程访问SAB之前能够访问它。(编辑:创建辅助进程的代码必须在一个单独的函数中(编辑2:返回一个指向SAB的数组)。(也许,这已经不可能了,你会告诉我) 初始代码如下所示: function init() { var code = `onmessage = function(event) { console.log('starting'); var buffer=
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
,然后显示启动
使用超时没有帮助。考虑以下代码:<代码>测试< /代码>:
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
,然后显示start
但是,将缓冲区分配给test()函数外部的全局数组可以完成此任务,即使没有超时
我的问题如下:
- 为什么在发送消息(=接收消息)后工作进程不直接启动。好了,工作人员有自己的事件队列,所以他们不应该依赖主堆栈变空
- 是否有详细说明工作人员在发送消息后何时开始工作的规范
- 是否有办法确保工作人员在再次访问SAB之前已启动,而不使用全局变量?(人们可能需要忙碌的等待,但我要小心……)可能没有办法,但我想确定
- 在完全并行运行的场景中,工作人员将能够 在邮件发布后立即处理该邮件。这显然是错误的 事实并非如此
- 大多数浏览器API(Worker就是这样的API)都使用回调队列来处理对API的调用。但如果这一点适用,那么信息将是 在执行超时回调之前已过帐/已处理
- 更进一步:如果我尝试在postMessage之后通过读取SAB直到它改变来忙着等待,一个值将阻止 无限编程。对我来说,这意味着浏览器可以 not在调用堆栈尽可能为空之前发布消息 我知道,这种行为没有记录,我无法解释
function init() {
//Unchanged
}
var array = init(); //global
function test (){
console.log('main');
//accessing the SAB again
};
它将开始
,然后主
打印到控制台
还有一点值得注意:如果我用Firefox浏览器调试代码(Chrome未测试)我得到了我想要的结果,而没有全局变量(在main
之前启动),有人能解释一下吗?根据MDN:
主页和辅助页之间传递的数据是复制的,而不是共享的。对象在传递给工作对象时被序列化,然后在另一端反序列化。页面和辅助对象不共享同一实例,因此最终结果是在每一端创建一个副本。大多数浏览器将此功能实现为结构化克隆
阅读更多关于
下面是一个与工作者共享缓冲区的基本代码。它创建了一个偶数值的数组(i*2)
,并将其发送给工作者。它用于更改缓冲区值
要确保工作进程已启动,您可以使用不同的消息
var code=document.querySelector('[type=“javascript/worker”]').textContent;
var blob=new blob([code],{“type”:'application/javascript'});
var blobUrl=URL.createObjectURL(blob);
var计数器=新工人(blobUrl);
var-sab;
var initBuffer=函数(msg){
sab=新的SharedArrayBuffer(16);
反邮件({
init:是的,
味精:味精,,
缓冲区:sab
});
};
var editArray=函数(){
var res=新的Int32Array(sab);
for(设i=0;i<4;i++){
原子学.存储(res,i,i*2);
}
console.log('Array edited',res);
};
initBuffer('Init buffer and start worker');
counter.onmessage=函数(事件){
日志(event.data.msg);
if(event.data.edit){
editArray();
//与工作者共享新缓冲区
counter.postMessage({buffer:sab});
//终端工人
counter.postMessage({end:true});
}
};代码>
var-sab;
self['onmessage']=函数(事件){
if(event.data.init){
postMessage({msg:event.data.msg,edit:true});
}
if(event.data.buffer){
sab=event.data.buffer;
var sharedArray=新的Int32Array(sab);
postMessage({msg:'共享数组:'+sharedArray});
}
if(event.data.end){
postMessage({msg:'Time to rest'});
}
};
为什么在sen[t](=接收到)消息后,工作进程不直接启动。另外,工作人员有自己的事件队列,因此他们不应该依赖主堆栈变空?
首先,即使您的工作线程对象在主线程中同步可用,但在实际的工作线程中,在能够处理您的消息之前,还需要执行以下操作:
- 它必须检索脚本内容。即使使用blobURI,它也是一个异步操作
- 它必须初始化整个js上下文,因此即使网络请求的速度非常快,这也会增加并行执行时间
- 它必须在主脚本执行之后等待事件循环帧来处理消息。即使初始化速度极快,它也会等待一段时间
所以在正常情况下,您的工作人员执行代码的可能性很小