网络工作者突然终止
Posted
技术标签:
【中文标题】网络工作者突然终止【英文标题】:Web workers terminating abruptly 【发布时间】:2016-09-03 03:01:19 【问题描述】:我在 chrome 上启动了一个 web worker,它有一个简单的函数,可以使用 setTimeout
重复调用。令人惊讶的是,在该函数被调用了大约 1000 次后,Web Worker 终止了。谁能解释为什么?我猜 chrome 正在做一些优化。
webworker.js
function hi()
postMessage('1');
setTimeout(hi, 1);
hi();
main.js
var blob = new Blob([code]);
var blobURL = window.URL.createObjectURL(blob);
var worker = new Worker(blobURL);
worker.onmessage = function(data)
console.log(data.data); // gets called around 1000 times and done
;
编辑: 在小提琴中转载: http://jsfiddle.net/meovfpv3/1/ onmessage 回调停止触发似乎需要任意长的时间,最快几秒,最长 +5 分钟
【问题讨论】:
当然我们可以解释原因,但不要让我们猜测。您必须展示一个可验证的最小示例,以便我们对其进行测试。 @IsmaelMiguel 我已经用 worker.js 代码更新了问题。基本上我用一个字符串做一个 blob 并把它交给工人 我遇到了同样的问题。我发现工人不会终止(用console.log
测试),onmessage 回调只是在某个时候停止被触发。非常奇怪且不可接受的浏览器行为! 桌子翻转
可能是消息队列已满或类似情况?
或者也许你的工人正在被垃圾收集,如果你不保留对他们的引用,这将会发生(我认为)。
【参考方案1】:
这是我对正在发生的事情的最佳猜测。通过每 1 毫秒从 Web Worker 发布一条消息,您要求主线程在 1 毫秒内处理每条发布的消息。
如果主线程无法在 1 毫秒内处理消息,即使它还没有完成对最后一条消息的处理,您仍然会向它发送一条新消息。我想这会将它放入等待处理的消息队列中。
现在,由于您从 Web Worker 发送消息的速度快于处理它们的速度,因此这个未处理消息的队列将会越来越大。在某个时候,Chrome 会举手说“队列中有太多消息”,而不是将新消息排队等待处理,而是丢弃它们。
这就是为什么如果您在超时中使用一个合理的数字(例如 100 毫秒),则在发送下一条消息之前,该消息有足够的时间进行处理,并且未处理的消息不会出现问题。
我创建了一个 jsFiddle,worker 向主线程发送消息,主线程将消息发送回 worker。如果在发送下一条消息之前该过程没有发生,则两个线程中的计数器将不匹配,并且 Web Worker 将终止。
http://jsfiddle.net/meovfpv3/3/
您可以看到,合理的 setTimeout 为 100 毫秒,所有消息在下一条消息发生之前都有足够的时间进行处理。
当您将 setTimeout 降低到 1 毫秒时,消息链在发送下一条消息之前没有时间完成,并且每个线程中的计数器最终会变得不同步,从而触发 if
子句并终止 Web Worker。
解决此问题的一种方法是,不要盲目地每 1ms 发布一条消息,无论最后一条消息是否已处理,只有在收到主线程返回的消息后才发布一条新消息。这意味着您只能以主线程可以处理它们的速度发布消息。
为了完整起见,这里是JSFiddle code的副本:
工人:
var counter2 = 0;
var rcvd = true;
function hi()
counter2++;
console.log("")
console.log("postMessage", counter2)
postMessage(counter2);
if (!rcvd)
self.close();
console.log("No message received");
rcvd = false;
setTimeout(hi, 1);
hi();
onmessage = function(e)
rcvd = true;
console.log("secondMessage", e.data);
主要:
var ww = document.querySelector('script[type="text/ww"]'),
code = ww.textContent,
blob = new Blob([code], type: 'text/javascript'),
blobUrl = URL.createObjectURL(blob),
worker = new Worker(blobUrl),
counter = 0;
worker.onmessage = function(e)
counter++;
console.log("onmessage:", counter);
worker.postMessage(e.data);
【讨论】:
但是 setTimeout 实际上不会每毫秒执行一次;充其量它会每 10-20 毫秒执行一次。 @torazaburo 没错,但setTimeout
仍会尽可能快地执行,这比它创建的消息的处理速度要快。
谢谢 Max,有趣的理论。但是,我的原始代码(与 OP 不同)使用了 85 毫秒的超时,因此 似乎 揭穿了这个理论。我什至不确定我添加的原始 jsfiddle 是否公正,因为问题似乎确实可以可靠地重现。不幸的是,由于存在机密代码,我无法在小提琴中提供我的整个用例。看起来你无论如何都会得到赏金(A表示努力!),除非其他人能指出问题所在。我怀疑这个问题是 Chrome v50.2xx 的新问题,与 Web Worker 优化有关
@AnsonKao 非常有趣。我想如果您对主线程中的消息处理功能进行时间测量,它会在 85 毫秒内始终处理消息,对吗? (如果超过平均水平,队列仍然会增长。)
我倾向于怀疑 Chrome 是否会因为 1000 条待处理消息而感到不堪重负,以至于它会举手杀死工人或丢弃消息。【参考方案2】:
首先,我无法解释一些观察结果,但很有趣,可能对某人有启发:
@Anson - 如果我将您的 jsFiddle 代码放入 Codepen(仍在 Chrome 中),则没有问题。 onmessage
回调一直在工作!
然后回到 jsFiddle... 它甚至无法将 setTimeout
更改为像 10 秒这样的长间隔,所以这不是工作人员发布消息的次数,而是 onmessage
回调之前的时间停止射击——这有很大的差异。
然后我找到了一些方法来让onmessage
处理程序在这个特定的例子中保持活跃:
$("#stop").on("click",function(e)e.preventDefault();worker.terminate(););
只需在定义onmessage
后添加console.log(worker)
。
受related question 中发布的答案启发,您也可以在定义onmessage
后简单地添加window.worker = worker
。
在所有情况下再次提及worker
似乎可以让它保持活力。
【讨论】:
当然可以。你有一个工人的参考。所以不会被垃圾回收。【参考方案3】:您是否尝试每 1 毫秒发布一次消息?那么你可能打算使用setInterval()
:
setInterval(function()
postMessage('1');
, 1);
编辑:我错误地看到了不存在的递归,只是因为我正在寻找它。我仍然会使用setInterval
而不是setTimeout
。
【讨论】:
有什么区别?hi()
不是递归调用自己。它在回调中调用自己,这是完全不同的事情。
没有区别(除了可能不相关的性能差异)。
@torazaburo 好吧,这是我犯的一个令人尴尬的错误!我想主要的区别是可读性?!以上是关于网络工作者突然终止的主要内容,如果未能解决你的问题,请参考以下文章
linux网络协议栈源码分析 - 传输层(TCP连接的终止)
linux网络协议栈源码分析 - 传输层(TCP连接的终止)
Docker 网络已禁用:警告:IPv4 转发已禁用。网络将无法正常工作