Web Worker被Chrome中的主线程阻止

时间:2014-12-16 21:39:34

标签: javascript multithreading google-chrome xmlhttprequest web-worker

我有Web Worker。我希望用它定期发送网络请求。我特别想要的一件事就是即使主要的JS执行线程被阻止(例如通过window.alert)也要发出这些请求。我正在使用Chrome 38。

但是,当我尝试在worker中发出网络请求时,UI线程似乎阻止了请求。这是一个用来说明问题的人为例子:

base.js:

var worker = new Worker("/worker.js");

setTimeout(function() {
    console.log("begin blocking");
    var startDt = new Date();
    var blockPeriod = 5000;
    var a;
    // Obviously we'd never actually do this, but this while loop
    // is a convenient way to create the problem case (a blocked main
    // thread).
    while ((new Date() - startDt) < blockPeriod) {
        a = 0;
    }
    console.log("stop blocking");
}, 3000);

worker.js:

var requestInterval = 1000;

var sendRequest = function() {
    console.log("Send request interval");

    var request = new XMLHttpRequest();
    request.open("GET", "/ping", true);

    request.onload = function() {
        if (request.status === 200){
            console.log(request.responseText)
        } else {
            console.log(request.status)
        }
    };

    request.onerror = function() {
        console.log("error")
    };

    request.send();

    setTimeout(sendRequest, requestInterval);
}

sendRequest();

我看到的结果是我们看到成功的HTTP请求三秒钟,直到阻止开始。此时,在阻塞结束之前我们没有看到任何记录到控制台的内容,此时我们看到五个“发送请求间隔”,然后是5个响应日志,如下所示:

Send request interval
{"pong": true}
Send request interval 
{"pong": true} 
Send request interval
{"pong": true}
Send request interval
{"pong": true}
begin blocking
stop blocking
5x Send request interval
5x {"pong": true}
Send request interval
{"pong": true}

我还在服务器日志中看到在该阻塞时间内没有发出请求,然后在阻塞期结束时大致同时收到这五个请求。

鉴于“发送请求间隔”连续五次出现,工作人员显然继续执行:如果不是,则不会通过排队进行下一次迭代。我还发现,如果我通过触发window.alert而不是循环旋转来阻止,我会以1秒为间隔从sendRequest的开头获取日志消息,然后获取响应处理程序日志消息一旦我停止阻止就会大批量。

在Firefox中,后台线程在这种情况下似乎完全停止(我在阻塞期间没有得到同一批五个请求排队)。但是,在这种情况下我只针对Chrome(我最终想要使用甚至不能在Firefox Workers中使用的WebSockets),所以我对此并不感兴趣。

所有这些都加在一起,这让我相信Web Workers中有一些活动被生成线程阻塞,有些活动没有(我最初看到了与WebSockets相同的行为)。具体来说,我想知道(如果有人知道):

  1. Chrome中的主要线程阻止了哪些工作人员活动?
  2. 有办法解决这个问题吗?我非常希望能够在Worker中建立WebSocket连接,然后来回继续PING / PONG,即使某些事情(例如警报/确认)确实阻止了主线程。
  3. 这都是胡说八道,我只是做些蠢事吗?

3 个答案:

答案 0 :(得分:5)

你的观察是正确的。当UI线程被阻止时,不会调度网络调用。

更糟糕的是,Chrome拥有最佳行为。当UI线程被阻止时,工作者发出XHR请求:

  • Chrome:所有请求都排队。在UI线程解除阻塞之前,浏览器实际上不会发出请求。从好的方面来说,工作线程仍可以自由运行。
  • Firefox:new XMLHttpRequest()阻止,直到UI线程解除阻塞。
  • IE:xhr.open()阻止,直到UI线程解除阻塞。

虽然Chrome幸运不会导致工作线程停止并等待(即使它不会获得任何数据),但当您尝试发出XHR请求时,Firefox和IE将导致工作线程在UI线程上等待

没有办法解决这个问题;您会感谢浏览器代表您提出请求。我没有使用WebSockets进行任何测试,但即使UI线程被阻止,他们也可能传递事件。在最坏的情况下,收到的消息将排队,直到UI线程解除阻塞。

答案 1 :(得分:2)

如果有人偶然发现这种情况,这种行为被确认为一个错误(对于&#34的松散定义;&#34;因为&#34;它的行为不应该像#34;),在Blink中,截至2015年2月:

https://code.google.com/p/chromium/issues/detail?id=443374

答案 2 :(得分:1)

我遇到了一个同样的问题,一个Web Worker执行一种保持活动状态:它会定期ping服务器,以通知该页面仍然有效。我也在工作程序中使用console.log,但是我确定这不是原因。

经过一些调查,我可以说问题可以解决两种不同的情况:

  1. 长时间的UI操作阻止了主线程,并且未执行工作程序的请求。这是由天芥菜说明的样品的情况。自2018年9月起,此问题仅在Firefox和IE上发生,因为当主线程被阻止时,Chrome和Edge可以正确处理worker上的活动。

为解决此问题,我正在考虑在开始任何长时间的操作之前向服务器发送特殊的ping,以通知他在操作完成之前不会收到我的任何东西。

  1. 主线程正在执行长时间异步ajax调用,并且工作人员的请求已排队。服务器上的某些设置阻止多个ajax调用并行运行:其他请求将排队,直到第一个ajax调用完成。该问题是特定于服务器的,因此它并不特别依赖于任何浏览器。

在我的情况下,此问题是由于ASP.NET会话状态锁定引起的:ASP.NET管道不会并发处理属于同一会话的请求,而是将它们排队并按顺序执行。这是详细的链接:http://tech-journals.com/jonow/2011/10/22/the-downsides-of-asp-net-session-state

将控制器的会话状态标记为ReadOnly将解决此问题,而在Web.config中完全禁用会话状态()将严重提高整个应用程序的性能。