【问题标题】:Allow running setInterval more than once per millisecond in nodejs允许在 nodejs 中每毫秒运行一次以上 setInterval
【发布时间】:2018-01-22 11:03:54
【问题描述】:

我有一个节点脚本,它应该利用单个节点进程可以获得的所有 CPU 资源。但是我发现 setInterval 太慢了。

果然我在文档中找到了这个:

当延迟大于 2147483647 或小于 1 时,延迟为 设置为 1。

来源:https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args

现在我想知道是否有办法进一步降低限制,或者是否有可以使用的替代功能。

我不能只使用普通循环,因为还有其他异步的东西需要同时运行。

编辑:
再说一遍:我不能只使用普通循环,因为还有其他异步的东西需要同时运行。 我不知道为什么这很难理解。

在正常循环运行时,您正在阻止其他所有内容的执行。将循环放在另一个异步执行的函数中并不重要。

这是什么意思?

让我们看一些例子:

setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background

while (true) {
    // do whatever
}

这段代码会做什么?它会阻止一切。 console.log('a') 不会被连续执行。

setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background
setTimeout(()=>{
    while (true) {
        // do whatever
    }
}, 1)

这也将在 while 循环开始时立即阻止间隔的执行。

【问题讨论】:

  • 那你为什么要把它放在setInterval 中呢?只需将其放入无限 forwhile 循环!
  • 你能发布你当前的代码吗?
  • 因为 Node 是单线程的。所以异步更像是中断。

标签: javascript node.js loops setinterval


【解决方案1】:

我相信问题属于 node 而不是浏览器。您可以使用以下一些选项(递归/循环)来减少延迟时间。

setImmediate

setImmediate - 安排在 I/O 事件回调之后“立即”执行回调。返回一个与clearImmediate() 一起使用的立即数。

当多次调用 setImmediate() 时,回调函数会按照创建顺序排队等待执行。每次事件循环迭代都会处理整个回调队列。如果即时计时器从正在执行的回调中排队,则在下一次事件循环迭代之前不会触发该计时器。

来自node指南:

setImmediatesetTimeout 相似,但表现不同 方式取决于它们何时被调用。

  • setImmediate() 旨在在当前轮询阶段完成后执行脚本。
  • setTimeout() 安排一个脚本在以毫秒为单位的最小阈值过去后运行。

process.nextTick

process.nextTick() 方法将回调添加到“下一个刻度 队列”。一旦事件循环的当前轮次运行到 完成,当前在下一个滴答队列中的所有回调将是 调用。

来自node指南

我们建议开发人员在所有情况下都使用 setImmediate(),因为它是 更容易推理(并且它导致代码与 更广泛的环境,例如浏览器 JS。)

【讨论】:

    【解决方案2】:

    感谢 Josh Lin 提出的只运行多个间隔的想法。我最终得到了 setIntervalclearInterval 的两个简单包装函数:

    function setInterval2(cb,delay) {
        if (delay >= 1)
            return [setInterval(cb,delay)];
        var intervalArr = [];
        var intervalCount = Math.round(1/delay);
        for (var i=0; i<intervalCount; i++)
            intervalArr.push(setInterval(cb,1));
        return intervalArr
    }
    
    function clearInterval2(intervalArr) {
        intervalArr.forEach(clearInterval);
    }
    

    它就像原来的函数一样工作:

    var count = 0;
    
    // run interval every 0.01 milliseconds:
    var foo = setInterval2(function(){
        count++;
    },0.01);
    
    // stop execution:
    clearInterval2(foo)
    

    【讨论】:

      【解决方案3】:

      1 setInterval 多次多跑!

      let count = 0,
        by = 100,
        _intervals = [],
        timelimit = 100
      for (let i = 0; i < by; i++) {
        _intervals[i] = setInterval(() => count++, 1)
      }
      setTimeout(() => {
        _intervals.forEach(x => clearInterval(x))
        console.log(`count:${count}`)
      }, timelimit)

      2.setTimeout recurser 少运行!

      let count = 0,
        go = true
      recurser()
      setTimeout(() => {
        go = false
        console.log(`count:${count}`)
      }, 100)
      
      function recurser() {
        count++
        go && setTimeout(recurser)
      }

      3.requestAnimationFrame 少跑!

      let count = 0,
        go = true,
        timelimit = 100
      step()
      setTimeout(() => {
        go = false,
          console.log(`count:${count}`)
      }, timelimit)
      
      function step() {
        count++
        go && requestAnimationFrame(step)
      }

      据我所知,多次运行setInterval,我相信while 会更多

      【讨论】:

        【解决方案4】:

        你问有没有可能

        在 nodejs 中每毫秒运行一次以上 setInterval

        正如您在问题中所指出的,setInterval 无法做到这一点,因为始终存在minimum delay of at least 1 ms in node.js。在浏览器中,通常有一个minimum delay of at least 10 ms

        但是,您想要实现的目标——重复运行 CPU 密集型代码而没有不必要的延迟——可以通过其他方式实现。

        正如The Reason 的回答中所述,setImmediate 是 node.js 中一个不错的选择。由于setImmediate 对浏览器的支持有限,并且是unlikely to be widely supported in the future,因此还有另一种方法也适用于浏览器。

        虽然浏览器对setIntervalsetTimeout 强制执行最小延迟,但setTimeout 的延迟是从设置计时器时开始执行的,而不是在运行时执行。如果我们重复使用setTimeout来调用CPU密集型代码,我们可以确保总是提前10-15毫秒设置一个定时器(如果代码至少需要10-15毫秒运行),从而减少实际延迟到 0 毫秒。

        下面的演示 sn-p 借用 this answer 的代码来演示如何使用预先设置的计时器可以使延迟小于强制执行的延迟。在我测试的浏览器中,这通常会导致 0 毫秒的延迟。

        // First: repeat runCPUForAtLeast50ms() 10 times
        // with standard repeated setTimeouts and enforced delays
        testTimeout(10, function(){
          // Then: repeat runCPUForAtLeast50ms() 10 times
          // using a repeated set of queued setTimeouts
          // circumventing the enforced delays
          testTimeout(10, false, true);
        });
        
        function testTimeout(repetitions, next, multiple) {
          var delays = [];
          var lastCheck;
          var extraTimers;
        
          function runner() {
            if(lastCheck){
              delays.push((+new Date) - lastCheck);
            }
            if(repetitions > 0) {
              //process chunk
              runCPUForAtLeast50ms();
              //set new timer
              setTimeout(runner);
            } else if(repetitions === 0) {
              //report result to console
              console.log((multiple? 
                'Repeated multiple timers delays: ' : 
                'Repeated single timer delays: ') + delays.join(', '));
              //invoke next() function if provided
              next && next();
            }
            repetitions--;
            lastCheck = +new Date;
          }
        
          setTimeout(runner);
        
          if(multiple){
           // make sure that there are always a fixed
           // number of timers queued by setting extra timers
           // at start
           extraTimers = 10;
           while(extraTimers--)
             setTimeout(runner);
          }
        }
        
        function runCPUForAtLeast50ms() {
          var d = (+new Date) + 50;
          while(+new Date < d);
        }

        【讨论】:

          【解决方案5】:

          我认为您可以使用async 模块解决您的问题...一种方法可能是:

          async.parallel([
            (callback) => {
              // do normal stuff
            },
            (callback) => {
              // do your loop
            }
          ], (err, results) => {
            // ...
          });
          

          但请考虑官方文档中的此注释...

          注意:并行是关于并行启动 I/O 任务,而不是关于 代码的并行执行。如果您的任务不使用任何计时器或 执行任何 I/O,它们实际上将被串行执行。任何 每个任务的同步设置部分将在 其他。 JavaScript 保持单线程。

          【讨论】:

            【解决方案6】:

            简而言之,你不能。 Javascript/Node 有一些限制,因为它是单线程应用程序。这就是你有异步中断的原因。

            长答案: 从计算机体系结构的角度来看,现代 CPU 和内核调度不是确定性的。如果您想要如此精细的控制,我建议您查看没有内核调度程序的 MCU 和嵌入式解决方案。因为,你的操作系统有很多其他进程和内核进程占用 CPU 时间,所以内核调度器必须不断调度不同的进程在 CPU 上运行,以满足许多不同的需求。

            即使设置为 1 毫秒,当您尝试测量时,它也可能不是 1 毫秒(确切时间取决于操作系统、硬件和计算机上运行的进程数量)。

            现在,如果要使用所有 CPU 资源,那是不可能的。

            但如果您想尽可能多地利用资源,您可以探索当前的编程模式。例如,你可以调度 100 万个线程(你的机器可能无法处理它),或者一些疯狂的大量进程,让你的调度器不断地把进程放在你的 CPU 上,这样就没有空闲时间和最大化 CPU 利用率。

            或者,您可以运行 CPU 压力测试,这些测试旨在简单地最大化您的 CPU 并使其保持在高温状态 - 确保您有适当的冷却解决方案。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-12-18
              • 2013-07-14
              • 2019-08-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-10-15
              • 1970-01-01
              相关资源
              最近更新 更多