node.js:setInterval()跳过调用

对于即将开始使用node.js的项目,我需要定期执行各种内务处理任务。 特别是每毫秒一些任务,其他每20毫秒(每秒50次),还有其他每秒。 所以我想过使用setInterval(),结果很有趣:很多函数调用被跳过了。

我使用的基准如下:

var counter = 0; var seconds = 0; var short = 1; setInterval(function() { counter ++; }, short); setInterval(function() { seconds ++; log('Seconds: ' + seconds + ', counter: ' + counter + ', missed ' + (seconds * 1000 / short - counter)); }, 1000); 

有一个很长的计时器,一秒钟和一个可以使用可变short调整,在这种情况下是1毫秒。 我们每秒都会打印出短周期中预期报价点的数量与短计数器更新的实际次数之间的差值。

下面是当短计时器为1毫秒时的行为:

 2012-09-14T23:03:32.780Z Seconds: 1, counter: 869, missed 131 2012-09-14T23:03:33.780Z Seconds: 2, counter: 1803, missed 197 2012-09-14T23:03:34.781Z Seconds: 3, counter: 2736, missed 264 ... 2012-09-14T23:03:41.783Z Seconds: 10, counter: 9267, missed 733 

许多函数调用被跳过。 这里是10毫秒:

 2012-09-14T23:01:56.363Z Seconds: 1, counter: 93, missed 7 2012-09-14T23:01:57.363Z Seconds: 2, counter: 192, missed 8 2012-09-14T23:01:58.364Z Seconds: 3, counter: 291, missed 9 ... 2012-09-14T23:02:05.364Z Seconds: 10, counter: 986, missed 14 

更好,但是大概一个函数调用每秒跳过。 而对于20毫秒:

 2012-09-14T23:07:18.713Z Seconds: 1, counter: 46, missed 4 2012-09-14T23:07:19.713Z Seconds: 2, counter: 96, missed 4 2012-09-14T23:07:20.712Z Seconds: 3, counter: 146, missed 4 ... 2012-09-14T23:07:27.714Z Seconds: 10, counter: 495, missed 5 

最后为100毫秒:

 2012-09-14T23:04:25.804Z Seconds: 1, counter: 9, missed 1 2012-09-14T23:04:26.803Z Seconds: 2, counter: 19, missed 1 2012-09-14T23:04:27.804Z Seconds: 3, counter: 29, missed 1 ... 2012-09-14T23:04:34.805Z Seconds: 10, counter: 99, missed 1 

在这种情况下,它会跳过很less的通话(33秒后差距增加到2秒,108秒后3秒。

这些数字有所不同,但在运行之间令人惊讶地一致:在9267,9259和9253秒的10秒之后,运行第一个1毫秒基准testing三次,结果出现延迟。

我没有find这个问题的参考。 有这么多引用Ressigpost和大量相关的JavaScript问题,但大多数人认为代码在浏览器中运行,而不是在node.js中运行。

现在是一个可怕的问题:这里发生了什么? 开玩笑吧 显然函数调用正在被跳过。 但是我看不到这个模式。 我认为长周期可能会阻止短周期,但在1毫秒的情况下没有任何意义。 短周期函数调用并不重叠,因为它们只是更新一个variables,并且node.js过程即使在1 ms的短周期内也接近5%的CPU。 负载平均值虽高,在0.50左右。 我不知道为什么有一千个电话会这么强调我的系统,不过,因为node.js 完美地处理了更多的客户端 ; setInterval()是CPU密集型的 (或者我做错了什么)。

一个明显的解决scheme是使用更长的定时器对函数调用进行分组,然后多次运行短循环函数来模拟更短的定时器。 然后使用长周期作为“扫把车”,使得在较低的时间间隔内错过任何呼叫。 例如:设置20 ms和1000 ms setInterval()调用。 对于1毫秒的呼叫:在20毫秒的回叫中呼叫20次。 对于1000毫秒的呼叫:检查20msfunction被调用的次数(例如47),做任何剩余的呼叫(例如3)。 但是这个scheme会有点复杂,因为通话可能会以有趣的方式重叠。 尽pipe可能看起来也不会有规律。

真正的问题是:可以用setInterval()或node.js中的其他定时器来做得更好吗? 提前致谢。

JavaScript中的SetInterval函数不准确。 您应该尝试使用高分辨率计时器。 在JavaScript中构build精确的计时器

看看这个文档: http : //nodejs.org/api/timers.html#timers_settimeout_callback_delay_arg

重要的是要注意,你的callback可能不会被精确地延迟几毫秒 – Node.js不能确保callback触发的确切时间,也不会触发sorting的东西。callback将被称为尽可能接近指定的时间。

这是因为应用程序代码阻止事件循环 所有计时器和I / O事件只能nextTick一个问题上处理。

你可以看到这个代码的行为:

 setInterval(function() { console.log(Date.now()); for (var i = 0; i < 100000000; i++) { } }, 1); 

尝试改变迭代次数并查看结果。

理想情况下,如果应用程序打勾将持续less于一个毫秒,计时器将被正确触发。 但是这在实际应用中是不可行的。

答案恰好是Vadim和zer02给出的结果,所以我在这里留言。 正如Vadim所说,系统无法应付过于频繁的更新,并且向系统添加一些负载不会有帮助。 或者说,运行时不能应付; 如果需要,系统应该能够每毫秒发射一次callback,但是由于一些不明原因的原因,往往不想这样做。

解决scheme是使用精确的定时器 ,如zer02评论。 不要被名字误导; 所使用的机制与setTimeout()是一样的,但是根据剩下的时间来调整延迟,直到定时器应该触发为止。 所以,如果时间结束,那么“准确的计时器”将会立即调用setTimeout(callback,0)。 令人惊讶的是,系统负载比使用setInterval()less得多:大约是CPU的2%,而不是5%,这是我非常不科学的例子。

这个简单的function可能派上用场:

 /** * A high resolution timer. */ function timer(delay, callback) { // self-reference var self = this; // attributes var counter = 0; var start = new Date().getTime(); /** * Delayed running of the callback. */ function delayed() { callback(delay); counter ++; var diff = (new Date().getTime() - start) - counter * delay; setTimeout(delayed, delay - diff); } // start timer delayed(); setTimeout(delayed, delay); } 

要使用,只需调用new timer(delay, callback); 。 (是的,我倒过来参数的顺序,因为首先callback非常恼人。)

最后一个注意事项:setTimeout(callback,delay)并不像我担心的那样使用recursion(如:等待一段时间,然后调用callback),它只是将callback放入一个队列中,来自全球的背景。

Interesting Posts