【问题标题】: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();

ma​​in.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 回调只是在某个时候停止被触发。非常奇怪且不可接受的浏览器行为! 桌子翻转
  • 可能是消息队列已满或类似情况?
  • 或者也许你的工人正在被垃圾收集,如果你不保留对他们的引用,这将会发生(我认为)。

标签: javascript google-chrome web worker


【解决方案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处理程序在这个特定的例子中保持活跃:

  • 在 html 中添加一个按钮/链接和一个处理程序(我使用 jQuery),该处理程序将在单击时终止工作程序。只需添加此代码即可修复它。 $("#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 好吧,这是我犯的一个令人尴尬的错误!我想主要的区别是可读性?!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-15
  • 2017-08-22
  • 2021-02-01
  • 2016-12-29
相关资源
最近更新 更多