Windows下的高精度定时器实现及精确时刻获取

通讯、VOIP、视频等领域的很多核心技术对时间精度的要求非常高,比如数据采集、时间同步、媒体流平滑控制、拥塞算法等等,很多技术都是以毫秒为单位来进行计算和控制的。但是Windows设计之初并不是以实时系统为目标的,所以Windows系统的时间精度一直不高,实际最小单位是15ms左右,导致的结果就是所有Windows的时间、线程相关的操作都无法以1ms来实现精确控制。

 

受影响的操作包括Sleep、GetTickCount、_ftime等等。比如你调用Sleep(2),期待2ms之后线程自动唤醒,但是实际结果可能是15ms甚至2x ms的时候才会唤醒,对于简单应用来说影响不大,但是对于精度要求非常高的系统来说,这样的问题就是非常致命的了。

 

代码思路如下:

1、高精度定时器。使用Singleton模式挂起请求Sleep的线程并统一管理,后台使用Windows MultiMedia SDK的定期回调函数不断检测并回复到时的线程,超时时间与当前时间采用QueryPerformanceCounter/QueryPerformanceFrequency的高精度计时,确保整体功能可靠性。

2、精确时刻获取。由于可以获取到毫秒级别的_ftime与GetTickCount都受到Windows系统时间精度影响,最小单位只有15ms,所以需要借助QueryPerformanceCounter/QueryPerformanceFrequency进行准确计时。代码首先根据_ftime获取起始时刻的精确刻度,然后根据差量计算当前的精确时刻。

 

代码中的Singleton模式可以找到很多实现,因此本文不进行详述

代码(VS2005 c++编译)

1、高精度定时器

 

 

[c-sharp] view plain copy
  1. #pragma once  
  2.  
  3. #include <Windows.h>  
  4. #include <list>  
  5. #include <akumaslab/system/singleton.hpp>  
  6.   
  7. namespace akumaslab{  
  8.     namespace time{  
  9.         using std::list;  
  10.   
  11.         class PreciseTimerProvider  
  12.         {  
  13.             struct WaitedHandle{  
  14.                 HANDLE threadHandle;  
  15.                 LONGLONG elapsed;//超时时间  
  16.             } ;  
  17.             typedef list< WaitedHandle > handle_list_type;  
  18.             typedef akumaslab::system::Singleton< PreciseTimerProvider > timer_type;  
  19.         public:  
  20.             PreciseTimerProvider(void):highResolutionAvailable(false), timerID(0)  
  21.             {  
  22.                 InitializeCriticalSection(&critical);  
  23.                 static LARGE_INTEGER systemFrequency;  
  24.                 if(0 != QueryPerformanceFrequency(&systemFrequency))  
  25.                 {  
  26.                     timeBeginPeriod(callbackInterval);  
  27.                     highResolutionAvailable = true;  
  28.                     countPerMilliSecond = systemFrequency.QuadPart/1000;  
  29.                     timerID = timeSetEvent(callbackInterval, 0, &PreciseTimerProvider::TimerFunc, NULL, TIME_PERIODIC);  
  30.                 }  
  31.             }  
  32.             //挂起当前线程  
  33.             //@milliSecond:超时时间,单位:毫秒  
  34.             bool suspendCurrentThread(int milliSecond)  
  35.             {  
  36.                 if(milliSecond <= 0)return false;  
  37.                 if (!highResolutionAvailable)return false;  
  38.                 HANDLE currentThreadHandle = GetCurrentThread();  
  39.                 HANDLE currentProcessHandle = GetCurrentProcess();  
  40.                 HANDLE realThreadHandle(0);  
  41.                 DuplicateHandle(currentProcessHandle, currentThreadHandle, currentProcessHandle, &realThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);  
  42.                 WaitedHandle item;  
  43.                 item.threadHandle = realThreadHandle;  
  44.                 LARGE_INTEGER now;  
  45.                 QueryPerformanceCounter(&now);  
  46.                 now.QuadPart += milliSecond * countPerMilliSecond;  
  47.                 item.elapsed = now.QuadPart;  
  48.                 EnterCriticalSection(&critical);  
  49.                 waitList.push_back(item);  
  50.                 LeaveCriticalSection(&critical);  
  51.                 //挂起线程  
  52.                 SuspendThread(realThreadHandle);  
  53.                 CloseHandle(realThreadHandle);  
  54.                 return true;  
  55.             }  
  56.             //恢复超时线程  
  57.             void resumeTimeoutThread()  
  58.             {  
  59.                 if (!highResolutionAvailable)return;  
  60.                 LARGE_INTEGER now;  
  61.                 QueryPerformanceCounter(&now);  
  62.                 EnterCriticalSection(&critical);  
  63.                 for (handle_list_type::iterator ir = waitList.begin(); ir != waitList.end(); )  
  64.                 {  
  65.                     WaitedHandle& waited = *ir;  
  66.                     if (now.QuadPart >= waited.elapsed)  
  67.                     {  
  68.                         ResumeThread(waited.threadHandle);  
  69.                         ir = waitList.erase(ir);  
  70.                         continue;  
  71.                     }  
  72.                     ir++;  
  73.                 }                                 
  74.                 LeaveCriticalSection(&critical);  
  75.             }  
  76.             ~PreciseTimerProvider(){  
  77.                 if (0 != timerID)  
  78.                 {  
  79.                     timeKillEvent(timerID);  
  80.                     timerID = 0;  
  81.                     timeEndPeriod(callbackInterval);  
  82.                 }  
  83.                 DeleteCriticalSection(&critical);  
  84.             }  
  85.         private:  
  86.   
  87.             static void CALLBACK TimerFunc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)  
  88.             {  
  89.                 static bool initialed = false;  
  90.                 if (!initialed)  
  91.                 {  
  92.                     if (initialWorkThread())  
  93.                     {  
  94.                         initialed = true;  
  95.                     }  
  96.                     else{  
  97.                         return;  
  98.                     }  
  99.                 }  
  100.                 timer_type::getRef().resumeTimeoutThread();  
  101.             }  
  102.             //调整定时器工作线程优先级  
  103.             static bool initialWorkThread()  
  104.             {  
  105.                 HANDLE realProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _getpid());  
  106.                 if (NULL == realProcessHandle)  
  107.                 {  
  108.                     return false;  
  109.                 }  
  110.                 if (0 == SetPriorityClass(realProcessHandle, REALTIME_PRIORITY_CLASS))  
  111.                 {  
  112.                     CloseHandle(realProcessHandle);  
  113.                     return false;  
  114.                 }  
  115.                 HANDLE currentThreadHandle = GetCurrentThread();  
  116.                 HANDLE currentProcessHandle = GetCurrentProcess();  
  117.                 HANDLE realThreadHandle(0);  
  118.                 DuplicateHandle(currentProcessHandle, currentThreadHandle, currentProcessHandle, &realThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);  
  119.                 SetThreadPriority(realThreadHandle, THREAD_PRIORITY_TIME_CRITICAL);  
  120.                 //必须关闭复制句柄  
  121.                 CloseHandle(realThreadHandle);  
  122.                 CloseHandle(realProcessHandle);  
  123.                 return true;  
  124.             }  
  125.         private:  
  126.             const static int callbackInterval = 1;  
  127.             CRITICAL_SECTION critical;  
  128.             MMRESULT timerID;  
  129.             LONGLONG countPerMilliSecond;  
  130.             bool highResolutionAvailable;  
  131.             handle_list_type waitList;  
  132.         };  
  133.         class PreciseTimer  
  134.         {  
  135.             typedef akumaslab::system::Singleton< PreciseTimerProvider > timer_type;  
  136.         public:  
  137.             static bool wait(int milliSecond)  
  138.             {  
  139.                 //static PreciseTimerProvider timer;  
  140.                 return timer_type::getRef().suspendCurrentThread(milliSecond);  
  141.             }  
  142.         };  
  143.     }  
  144. }  
 

DEMO

  1. int interval = 1;  
  2. int repeatCount = 50;  
  3. cout << getCurrentTime() << "test begin" << endl;  
  4. unit.reset();  
  5. for (int i = 0; i < repeatCount; i++)  
  6. {  
  7.     akumaslab::time::PreciseTimer::wait(interval);  
  8.     cout << getCurrentTime() << "/" << getNewTime() << " used " << unit.getPreciseElapsedTime() << " ms" << endl;  
  9.     unit.reset();  
  10. }  

 

2、精确时刻获取

  1. #pragma once  
  2.   
  3. #include <sys/timeb.h>  
  4. #include <time.h>  
  5. #include <Windows.h>  
  6. #include <akumaslab/system/singleton.hpp>  
  7.   
  8. namespace akumaslab{  
  9.     namespace time{  
  10.         struct HighResolutionTime  
  11.         {  
  12.             int year;  
  13.             int month;  
  14.             int day;  
  15.             int hour;  
  16.             int min;  
  17.             int second;  
  18.             int millisecond;  
  19.         };  
  20.         class CurrentTimeProvider  
  21.         {  
  22.         public:  
  23.             CurrentTimeProvider():highResolutionAvailable(false), countPerMilliSecond(0), beginCount(0)  
  24.             {  
  25.                 static LARGE_INTEGER systemFrequency;  
  26.                 if(0 != QueryPerformanceFrequency(&systemFrequency))  
  27.                 {  
  28.                     highResolutionAvailable = true;  
  29.                     countPerMilliSecond = systemFrequency.QuadPart/1000;  
  30.                     _timeb tb;  
  31.                     _ftime_s(&tb);  
  32.                     unsigned short currentMilli = tb.millitm;  
  33.                     LARGE_INTEGER now;  
  34.                     QueryPerformanceCounter(&now);  
  35.                     beginCount = now.QuadPart - (currentMilli*countPerMilliSecond);  
  36.                 }  
  37.             };  
  38.             bool getCurrentTime(HighResolutionTime& _time)  
  39.             {  
  40.                 time_t tt;  
  41.                 ::time(&tt);  
  42.                 tm now;  
  43.                 localtime_s(&now, &tt);  
  44.                 _time.year = now.tm_year + 1900;  
  45.                 _time.month = now.tm_mon + 1;  
  46.                 _time.day = now.tm_mday + 1;  
  47.                 _time.hour = now.tm_hour;  
  48.                 _time.min = now.tm_min;  
  49.                 _time.second = now.tm_sec;  
  50.                 if (!highResolutionAvailable)  
  51.                 {  
  52.                     _time.millisecond = 0;  
  53.                 }  
  54.                 else{  
  55.                     LARGE_INTEGER qfc;  
  56.                     QueryPerformanceCounter(&qfc);  
  57.                     _time.millisecond = (int)((qfc.QuadPart - beginCount)/countPerMilliSecond)%1000;  
  58.                 }  
  59.                 return true;  
  60.             }  
  61.         private:  
  62.             bool highResolutionAvailable;  
  63.             LONGLONG countPerMilliSecond;  
  64.             LONGLONG beginCount;  
  65.         };  
  66.         class CurrentTime  
  67.         {  
  68.         public:  
  69.             static bool get(HighResolutionTime& _time)  
  70.             {  
  71.                 return akumaslab::system::Singleton< CurrentTimeProvider >::getRef().getCurrentTime(_time);  
  72.             }  
  73.         };  
  74.     }  
  75. }  

DEMO:

  1. HighResolutionTime time;  
  2. CurrentTime::get(time);  
  3. const int size = 20;  
  4. char buf[size] = {0};  
  5. _snprintf_s(buf, size, size, "%02d:%02d %02d:%02d:%02d.%03d ", time.month, time.day, time.hour, time.min, time.second, time.millisecond);  

 

测试结果如下,下图是高精度计时器按1ms进行Sleep的结果,左侧为使用_ftime计时,右侧为使用精确时刻计时,总体来说,虽然无法达到100%可靠,但是相对原来的15ms已经有较大提升,期望Windows能够尽快提供真正的高精度时间管理技术

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 高精度定时器是指能够实现毫秒级别或更高精度定时器。在MFC中实现高精度定时器可以通过以下步骤实现: 1.创建一个窗口,并通过SetTimer函数设置一个定时器。该函数有两个参数,一个是定时器ID号,一个是定时器的时间间隔。 2.在窗口类中添加一个成员变量,用于保存已经过去的时间。我们可以在WM_TIMER消息处理函数中,每次处理时递增该变量。 3.通过该变量计算出所需的时间,然后进行相应的操作。例如,我们可以将该变量转化为分钟和秒钟,然后在窗口上显示出来。 4.为了提高定时器的精度,可以通过Win32 API函数timeGetTime获取系统时间,然后在WM_TIMER消息处理函数中计算与上一次时间间隔,从而更加精确地计算已经过去的时间。 需要注意的是,高精度定时器会占用系统资源,并且可能存在时间误差。因此,在实现时需要考虑这些因素,并根据实际需求进行调整。 ### 回答2: 高精度定时器是一种能够实现较为精确的时间计量和延时控制的技术,而MFC(Microsoft Foundation Classes)则是基于Windows操作系统的C++类库,提供了GUI界面开发所需要的各种类、函数和控件等工具。将两者结合使用,可以实现用MFC编写的应用程序对时间的更加准确的控制或监测,如毫秒或微秒级别的时间计算和处理等。 要在MFC中实现高精度定时器功能,可以考虑使用Win32 API中提供的计时器函数来进行实现。具体实现步骤如下: 1. 定义计时器变量和时间变量。例如: UINT_PTR m_TimerID; // 计时器ID DWORD m_dwStartTime; // 记录开始时间 DWORD m_dwCurrentTime; // 记录当前时间 DWORD m_dwElapsedTime; // 记录已过时间 2. 创建计时器并开始计时。可以在窗口初始化函数中添加如下代码: m_TimerID = SetTimer(1, 1, NULL); // 1ms间隔 m_dwStartTime = GetTickCount(); // 记录开始时间 3. 处理计时器消息。在窗口消息响应函数中,添加对WM_TIMER消息的处理,如: case WM_TIMER: { m_dwCurrentTime = GetTickCount(); // 记录当前时间 m_dwElapsedTime = m_dwCurrentTime - m_dwStartTime; // 计算已过时间 // 这里可以根据需要进行时间数据的显示、处理等其他操作 } break; 4. 在窗口关闭时停止计时器。可以在窗口关闭函数中添加如下代码: KillTimer(m_TimerID); 以上就是使用高精度定时器实现MFC的简单示例。需要注意的是,由于不同计算机的性能和Windows操作系统的版本等因素可能会影响计时器的精度和稳定性,因此在实际应用中需要针对具体需求进行测试和调整。 ### 回答3: 高精度定时器可以通过MFC的计时器来实现。MFC的计时器是基于Windows API的定时器实现的。Windows API提供了一个SetTimer函数,用于设置定时器。MFC的CWnd类继承了Windows API的CWnd类,在此基础上提供了一系列的计时器函数。 使用MFC计时器,首先需要在类声明中添加一个计时器ID,具体实现可以如下: #define TIMER_ID 1 class CMyDlg : public CDialog { public: CMyDlg(CWnd* pParent = NULL); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_MYDLG_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); protected: HICON m_hIcon; int m_nCount; afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg void OnBnClickedButtonStart(); afx_msg void OnBnClickedButtonStop(); DECLARE_MESSAGE_MAP() }; 在类声明中添加了一个计时器ID为1。同时,在消息映射中,添加了一个响应定时器事件的函数OnTimer。 void CMyDlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == TIMER_ID) { m_nCount++; //每次增加计数 } CDialog::OnTimer(nIDEvent); } OnTimer函数响应计时器事件,其中nIDEvent就是计时器ID。在函数中,我们可以编写计时器事件响应的代码,这里是每次增加计数。 在对话框初始化时就设置计时器: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 SetTimer(TIMER_ID, 500, NULL); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } 在OnInitDialog函数中添加代码SetTimer(TIMER_ID, 500, NULL);就可以设置一个500ms的计时器了。 当然,在对话框关闭时,还要记得取消计时器: void CMyDlg::OnBnClickedButtonStop() { // TODO: 在此添加控件通知处理程序代码 KillTimer(TIMER_ID); } 这样,一个MFC的高精度定时器实现了。其实,在MFC中,还可以使用CWnd::SetTimer来设置定时器,不过与SetTimer函数类似,使用时也需要取消,不然会造成内存泄漏。同时,MFC还提供了更为灵活的计时器功能,可以用来处理其他复杂的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值