【问题标题】:NextTick() does not working [Interleaving CPU-intensive task with other events]NextTick() 不起作用 [将 CPU 密集型任务与其他事件交错]
【发布时间】:2015-01-31 12:07:50
【问题描述】:

我正在阅读http://howtonode.org/understanding-process-next-tick 但是,它附带的代码并没有实现 CPU 密集型任务。

我尝试编写我的版本。但这是错误的。

compute() 执行后没有任何 IO 服务。

所以,我的问题是:在这种情况下使用 nextTick() 函数的正确方法是什么?

compute() 执行时我不想阻塞 IO。

var http = require('http');

function compute() {

    // performs complicated calculations continuously
    // ...
    var result = 0;
    for(var i = 0; i < 1000000; i++){
        for(var j = i;  j < 1000000; j++){
            result += i + j;
        }
    }
    process.nextTick(compute);
}

http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World');
}).listen(5000, '127.0.0.1');


compute();

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

    nextTick 只是安排你的函数在下一个tick of the event loop 上被调用。它没有赋予该函数神奇的非阻塞属性; JavaScript 仍然是单线程的。如果函数阻塞(通过执行大量 CPU 密集型工作),它仍然会导致 I/O 事件排队,直到函数完成。

    如果您需要做 CPU 密集型工作,do it in a worker process

    【讨论】:

      【解决方案2】:

      nextTick 是一个阻塞调用(至少在节点 v0.10.29 中,我对此进行了测试),它阻止其他事件运行。使用 setImmediate()

      【讨论】:

      • nextTick 不会阻止。
      • 使用 nextTick 排队的函数绕过事件队列,并在当前运行的 continable 退出后立即运行。因此,如果它们像上面那样自递归,那么它们会阻止 http 服务器处理请求。
      • 那是另一回事。 nextTick 本身不会阻塞。它安排某些东西“稍后”运行,并将控制权立即传递回您的代码。如果一个函数像 OP 的例子一样是 CPU 密集型的,那么精确调度调用的时间并不重要;它仍然会阻止 I/O 事件在运行时触发。
      • 不正确。阻塞调用和使事件循环饿死的函数之间有一个微妙但重要的区别,即使两者都很糟糕。阻塞调用是在工作完成之前控制不会返回给调用用户代码的调用。另一方面,需要很长时间执行的函数会使事件循环饿死(就像实际的阻塞调用一样),但不会被用户代码直接调用,因此不会阻止控制返回到事件循环的中间。用户功能。说nextTick 阻塞是不正确的,因为控制总是立即从它返回。
      • nextTicksetImmediatesetTimeout 之间的唯一区别是 何时 回调计划运行。分别在下一个滴答开始时、当前事件队列结束时以及未来某个挂起时间。如果 callback 通过花费很长时间来完成工作而阻塞了事件循环,则称它使事件循环饥饿,因为它阻止了队列中的其他事件被调度。此外,它只阻塞 JS 线程。其他(本机)线程没有被阻塞;他们继续将事件添加到队列中。
      【解决方案3】:

      setImmediate 会更好地工作,正如我在博客文章 setTimeout and Friends 中所解释的那样,因为它将允许 IO 任务在再次锁定主执行线程以完全运行 compute 之前运行。但是正如发布的其他答案所暗示的那样,思考这个问题的方法不是“nextTick 不起作用”,而是“哎呀,我正在尝试做你绝对不能在节点中做的唯一事情之一。 js 应用程序,我得到了我被警告过的结果”。您不能在节点中占用执行线程,因为它是协作多任务处理。将计算分成小块,使用外部进程助手,将某些内容拆分为支持的 C++ 库等。

      【讨论】:

      • 对 CodeFarmer 公平地说,在我看来负载是故意的,用于测试并发处理。除了 javascript/node 不支持这种并行性。如果 compute() 循环仅循环 1e6 次而不是 1e12 次,并使用 setImmediate() 而不是 nextTick,则实验会成功,程序将同时使用 100% 并无缝处理请求
      • @Andras,不,使用setImmediate 不会解决问题。 compute 仍然会饿死 I/O,程序将无法同时处理多个请求。
      • 查看关于 1e6 而不是 1e12 的部分,这将使 compute() 在一毫秒内运行。给我一些信任,好吗
      【解决方案4】:

      我会稍微重写你的代码。假设我们需要处理 1000000 个项目,并且有一个(cpu bound,但有时可以调用)函数computeItem() 和 io-bound postItem()。我们希望在后台处理尽可能多的项目,但仍然具有响应式事件循环。为简单起见,没有使用外部工作者/队列/服务。可能的解决方案:

      var desiredLatency = 10; // ms
      
      function doBackgroundWork() {
        var start = new Date();
        var end;
        var item; 
        while (item = computeItem()) {
          postItem(item);
          if (end - start >= desiredLatency) {
            setImmediate(doBackgroundWork); // resume at next event loop invocation after processing other handlers
          }
        }
      }
      
      http.createServer(function(req, res) {
          res.writeHead(200, {'Content-Type': 'text/plain'});
          res.end('Hello World');
      }).listen(5000, '127.0.0.1');
      doBackgroundWork();
      

      【讨论】:

      • 能给我看看computeItem函数和postItem函数的代码吗?
      • "computeItem" 是 CPU 密集型同步代码所在的位置
      猜你喜欢
      • 2014-08-03
      • 2011-06-15
      • 1970-01-01
      • 2016-06-04
      • 2017-04-10
      • 2021-01-02
      • 2013-07-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多