2015-09-26 178 views
1

我想在每毫秒計算一些數據的線程內運行一個循環。但我在睡眠功能方面遇到問題。它睡得太久了。每秒運行一次循環sleep_for

我創建在Visual Studio中基本控制檯應用程序:

#include <windows.h> 
#include <iostream> 
#include <chrono> 
#include <thread> 

using namespace std; 
typedef std::chrono::high_resolution_clock Clock; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int iIdx = 0; 
    bool bRun = true; 

    auto aTimeStart = Clock::now(); 

    while (bRun){ 
     iIdx++; 
     if (iIdx >= 500) bRun = false; 

     //Sleep(1); 
     this_thread::sleep_for(chrono::microseconds(10)); 
    } 

    printf("Duration: %i ms\n", chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - aTimeStart).count()); 

    cin.get(); 

    return 0; 
} 

此打印出:持續時間:5000毫秒 同樣的結果被打印,當我使用的Sleep(1);

我期望的持續時間爲500毫秒,而不是5000毫秒。我在這裏做錯了什麼?

更新:


我使用Visual Studio 2013年現在,我已經安裝了Visual Studio 2015年,它的精 - 打印出:持續時間:500毫秒(有時其527毫秒)。

但是,這sleep_for仍然不是很準確,所以我會尋找其他解決方案。

+0

C++標準時鍾在很大程度上取決於操作系統的基本時鐘,也許Windows不有這樣一個高分辨率的時鐘? –

+0

適用於我的機器™。你使用的是什麼版本的Visual Studio? VS2015之前'std :: chrono :: high_resolution_clock'沒有很好的分辨率。 Boost的替代品有很大的下降,你可以嘗試在Boost.Chrono中使用 – melak47

+0

你也在while循環下執行操作,這將需要一定的循環次數。只是爲了檢查你是否修改iIdx ++到++ iIdx是否會在輸出中產生任何影響? – Invictus

回答

4

流行操作系統使用的典型時間片比1ms長(比如20ms左右)。 sleep設置了您希望您的線程暫停而不是最長時間的最小值。一旦你的線程變得可運行,它將在下一次安排時由操作系統決定。

如果你需要這個級別的準確性,你可能需要一個實時操作系統,或者在你的線程上設置一個非常高的優先級(這樣它可以搶佔其他任何東西),或者將你的代碼寫入內核,或者使用忙碌的等待。

但是你真的需要每ms計算一次嗎?這種時序要求通常來自硬件。如果你稍後將計算結合起來會出現什麼問題?

0

我在做什麼錯在這裏?

試圖使用sleep進行精確計時。

sleep(n)不會暫停您的線程精確n時間,然後立即繼續。

sleep(n)產生線程回調度的控制,並表明你不想控制權交還給直到至少n時代已經過去了。

現在,調度程序已經將線程處理時間分成時間片,而這些時間片通常約爲25毫秒左右。這是最低你可以期待你的睡眠運行。

sleep只是這項工作的錯誤工具。 千萬不要用它來精確調度。

0

此線程相當陳舊,但也許有人仍然可以使用此代碼。

它是爲C++ 11編寫的,我在Ubuntu 15.04上測試過它。

class MillisecondPerLoop 
    { 
    public: 
    void do_loop(uint32_t loops) 
     { 
     int32_t time_to_wait = 0; 
     next_clock = ((get_current_clock_ns()/one_ms_in_ns) * one_ms_in_ns); 
     for (uint32_t loop = 0; loop < loops; ++loop) 
     { 
     on_tick(); 

     // Assume on_tick takes less than 1 ms to run 

     // calculate the next tick time and time to wait from now until that time 
     time_to_wait = calc_time_to_wait(); 

     // check if we're already past the 1ms time interval 
     if (time_to_wait > 0) 
      { 
      // wait that many ns 
      std::this_thread::sleep_for(std::chrono::nanoseconds(time_to_wait)); 
      } 
     ++m_tick; 
     } 
    } 

private: 
    void on_tick() 
    { 
    // TEST only: simulate the work done in every tick 
    // by waiting a random amount of time 
     std::this_thread::sleep_for(std::chrono::microseconds(distribution(generator))); 
    } 

    uint32_t get_current_clock_ns() 
    { 
    return std::chrono::duration_cast<std::chrono::nanoseconds>(
    std::chrono::system_clock::now().time_since_epoch()).count(); 
    } 

    int32_t calc_time_to_wait() 
    { 
    next_clock += one_ms_in_ns; 
    return next_clock - get_current_clock_ns(); 
    } 

    static constexpr uint32_t one_ms_in_ns = 1000000L; 

    uint32_t m_tick; 
    uint32_t next_clock; 
}; 

一個典型的運行顯示了一個相當精確的1ms循環,其中1-3微秒的誤差。如果CPU速度更快,那麼您的PC可能會比這更精確。

下面是典型的輸出:

One Second Loops: 
     Avg (ns)  ms err(ms) 
    [ 0] 999703 0.9997 0.0003 
    [ 1] 999888 0.9999 0.0001 
    [ 2] 999781 0.9998 0.0002 
    [ 3] 999896 0.9999 0.0001 
    [ 4] 999772 0.9998 0.0002 
    [ 5] 999759 0.9998 0.0002 
    [ 6] 999879 0.9999 0.0001 
    [ 7] 999915 0.9999 0.0001 
    [ 8] 1000043 1.0000 -0.0000 
    [ 9] 999675 0.9997 0.0003 
    [10] 1000120 1.0001 -0.0001 
    [11] 999606 0.9996 0.0004 
    [12] 999714 0.9997 0.0003 
    [13] 1000171 1.0002 -0.0002 
    [14] 999670 0.9997 0.0003 
    [15] 999832 0.9998 0.0002 
    [16] 999812 0.9998 0.0002 
    [17] 999868 0.9999 0.0001 
    [18] 1000096 1.0001 -0.0001 
    [19] 999665 0.9997 0.0003 
Expected total time: 20.0000ms 
Actual total time : 19.9969ms 

我有一個更詳細的寫在這裏: https://arrizza.org/wiki/index.php/One_Millisecond_Loop