簡體   English   中英

node.js:setInterval() 跳過調用

[英]node.js: setInterval() skipping calls

對於即將到來的 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 ms。 我們每一秒都打印短周期中預期的滴答數與短計數器更新的實際次數之間的差值。

以下是短定時器為 1 ms 時的行為:

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

在這種情況下,它跳過很少的調用(間隔在 33 秒后增加到 2,在 108 秒后增加到 3。

數字有所不同,但在運行之間驚人地一致:運行第一個 1 毫秒基准測試 3 次,在 9267、9259 和 9253 10 秒后產生延遲。

我沒有找到這個特定問題的參考。 很多引用的 Ressig 帖子和許多相關的 JavaScript 問題,但大多數假設代碼在瀏覽器中運行,而不是在 node.js 中運行。

現在問一個可怕的問題:這里發生了什么? 開個玩笑; 顯然函數調用被跳過。 但我看不到這種模式。 我認為長周期可能會阻止短周期,但在 1 毫秒的情況下沒有任何意義。 短周期函數調用不會重疊,因為它們只是更新一個變量,即使是 1 毫秒的短周期,node.js 進程也接近 5% 的 CPU。 不過,平均負載很高,約為 0.50。 我不知道為什么一千個調用對我的系統造成如此大的壓力,因為 node.js 完美地處理了更多的客戶端 setInterval() 確實是 CPU 密集型的(或者我做錯了什么)。

一個明顯的解決方案是使用更長的計時器對函數調用進行分組,然后多次運行短周期函數調用來模擬更短的計時器。 然后將長周期用作“掃帚馬車”,使任何在較低間隔中錯過的電話。 示例:設置 20 毫秒和 1000 毫秒的 setInterval() 調用。 對於 1 ms 調用:在 20 ms 回調中調用它們 20 次。 對於 1000 ms 調用:檢查 20ms 函數被調用了多少次(例如 47),執行任何剩余的調用(例如 3)。 但是這個方案會有點復雜,因為調用可能會以有趣的方式重疊; 雖然它可能看起來像它,但它也不會是常規的。

真正的問題是:使用 setInterval() 或 node.js 中的其他計時器可以做得更好嗎? 提前致謝。

JavaScript 中的 SetInterval 函數不准確。 您應該嘗試使用高分辨率計時器。 在 javascript 中構建准確的計時器

看看這個文檔: http : //nodejs.org/api/timers.html#timers_settimeout_callback_delay_arg

重要的是要注意,您的回調可能不會在確切的延遲毫秒內被調用 - Node.js 不保證回調將觸發的確切時間,也不保證將觸發的順序。回調將被調用為盡可能接近指定的時間。

這是因為應用程序代碼阻塞了事件循環。 所有計時器和 I/O 事件只能在nextTick上處理。

您可以使用以下代碼查看此行為:

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

嘗試更改迭代次數並查看結果。

理想情況下,如果應用程序滴答持續時間少於 1 毫秒,則將准確觸發計時器。 但這在實際應用中是不可行的。

答案恰好是 Vadim 和 zer02 給出的答案的組合,所以我在這里留下了一篇文章。 正如 Vadim 所說,系統無法應對過於頻繁的更新,給系統增加一些負載也無濟於事。 或者更確切地說,運行時無法應對; 如果需要,系統應該能夠每毫秒觸發一次回調,但由於某些無法解釋的原因,它通常不想要。

解決方案是使用准確的計時器,正如 zer02 評論的那樣。 不要被名字誤導; 使用的機制與 setTimeout() 相同,但延遲會根據計時器觸發之前的剩余時間進行調整。 因此,如果時間結束,那么“准確計時器”將調用立即運行的 setTimeout(callback, 0)。 令人驚訝的是,系統負載比 setInterval() 少:在我非常不科學的樣本中,大約是 CPU 的 2% 而不是 5%。

這個簡單的函數可能會派上用場:

/**
 * A high resolution timer.
 */
function timer(delay, callback)
{
    // self-reference
    var self = this;

    // attributes
    var counter = 0;
    self.running = true;
    var start = new Date().getTime();

    /**
     * Delayed running of the callback.
     */
    function delayed()
    {
        callback(delay);
        counter ++;
        var diff = (new Date().getTime() - start) - counter * delay;
        if (!self.running) return;
        setTimeout(delayed, delay - diff);
    }

    // start timer
    delayed();
    setTimeout(delayed, delay);
}

要使用,只需調用new timer(delay, callback); . (是的,我顛倒了參數的順序,因為首先進行回調非常煩人。)要停止它,請設置timer.running = false

最后一個注意事項:setTimeout(callback, delay) 沒有像我擔心的那樣使用遞歸(例如:等待一段時間,然后調用回調),它只是將回調放在一個隊列中,該隊列將在輪到運行時調用來,在全球范圍內。

我禁用調試器並再次嘗試。它對我來說很好用

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM