您的位置:首页 > 其它

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

2017-06-09 17:49 363 查看
通讯、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 plaincopy

#pragma once  
 
#include <Windows.h>  
#include <list>  
#include <akumaslab/system/singleton.hpp>  
  
namespace akumaslab{  
    namespace time{  
        using std::list;  
  
        class PreciseTimerProvider  
        {  
            struct WaitedHandle{  
                HANDLE threadHandle;  
                LONGLONG elapsed;//超时时间  
            } ;  
            typedef list< WaitedHandle > handle_list_type;  
            typedef akumaslab::system::Singleton< PreciseTimerProvider > timer_type;  
        public:  
            PreciseTimerProvider(void):highResolutionAvailable(false), timerID(0)  
            {  
                InitializeCriticalSection(&critical);  
                static LARGE_INTEGER systemFrequency;  
                if(0 != QueryPerformanceFrequency(&systemFrequency))  
                {  
                    timeBeginPeriod(callbackInterval);  
                    highResolutionAvailable = true;  
                    countPerMilliSecond = systemFrequency.QuadPart/1000;  
                    timerID = timeSetEvent(callbackInterval, 0, &PreciseTimerProvider::TimerFunc, NULL, TIME_PERIODIC);  
                }  
            }  
            //挂起当前线程  
            //@milliSecond:超时时间,单位:毫秒  
            bool suspendCurrentThread(int milliSecond)  
            {  
                if(milliSecond <= 0)return false;  
                if (!highResolutionAvailable)return false;  
                HANDLE currentThreadHandle = GetCurrentThread();  
                HANDLE currentProcessHandle = GetCurrentProcess();  
                HANDLE realThreadHandle(0);  
                DuplicateHandle(currentProcessHandle, currentThreadHandle, currentProcessHandle, &realThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);  
                WaitedHandle item;  
                item.threadHandle = realThreadHandle;  
                LARGE_INTEGER now;  
                QueryPerformanceCounter(&now);  
                now.QuadPart += milliSecond * countPerMilliSecond;  
                item.elapsed = now.QuadPart;  
                EnterCriticalSection(&critical);  
                waitList.push_back(item);  
                LeaveCriticalSection(&critical);  
                //挂起线程  
                SuspendThread(realThreadHandle);  
                CloseHandle(realThreadHandle);  
                return true;  
            }  
            //恢复超时线程  
            void resumeTimeoutThread()  
            {  
                if (!highResolutionAvailable)return;  
                LARGE_INTEGER now;  
                QueryPerformanceCounter(&now);  
                EnterCriticalSection(&critical);  
                for (handle_list_type::iterator ir = waitList.begin(); ir != waitList.end(); )  
                {  
                    WaitedHandle& waited = *ir;  
                    if (now.QuadPart >= waited.elapsed)  
                    {  
                        ResumeThread(waited.threadHandle);  
                        ir = waitList.erase(ir);  
                        continue;  
                    }  
                    ir++;  
                }                                 
                LeaveCriticalSection(&critical);  
            }  
            ~PreciseTimerProvider(){  
                if (0 != timerID)  
                {  
                    timeKillEvent(timerID);  
                    timerID = 0;  
                    timeEndPeriod(callbackInterval);  
                }  
                DeleteCriticalSection(&critical);  
            }  
        private:  
  
            static void CALLBACK TimerFunc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)  
            {  
                static bool initialed = false;  
                if (!initialed)  
                {  
                    if (initialWorkThread())  
                    {  
                        initialed = true;  
                    }  
                    else{  
                        return;  
                    }  
                }  
                timer_type::getRef().resumeTimeoutThread();  
            }  
            //调整定时器工作线程优先级  
            static bool initialWorkThread()  
            {  
                HANDLE realProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _getpid());  
                if (NULL == realProcessHandle)  
                {  
                    return false;  
                }  
                if (0 == SetPriorityClass(realProcessHandle, REALTIME_PRIORITY_CLASS))  
                {  
                    CloseHandle(realProcessHandle);  
                    return false;  
                }  
                HANDLE currentThreadHandle = GetCurrentThread();  
                HANDLE currentProcessHandle = GetCurrentProcess();  
                HANDLE realThreadHandle(0);  
                DuplicateHandle(currentProcessHandle, currentThreadHandle, currentProcessHandle, &realThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);  
                SetThreadPriority(realThreadHandle, THREAD_PRIORITY_TIME_CRITICAL);  
                //必须关闭复制句柄  
                CloseHandle(realThreadHandle);  
                CloseHandle(realProcessHandle);  
                return true;  
            }  
        private:  
            const static int callbackInterval = 1;  
            CRITICAL_SECTION critical;  
            MMRESULT timerID;  
            LONGLONG countPerMilliSecond;  
            bool highResolutionAvailable;  
            handle_list_type waitList;  
        };  
        class PreciseTimer  
        {  
            typedef akumaslab::system::Singleton< PreciseTimerProvider > timer_type;  
        public:  
            static bool wait(int milliSecond)  
            {  
                //static PreciseTimerProvider timer;  
                return timer_type::getRef().suspendCurrentThread(milliSecond);  
            }  
        };  
    }  
}  

 
DEMO

[cpp]
view plaincopy

int interval = 1;  
int repeatCount = 50;  
cout << getCurrentTime() << "test begin" << endl;  
unit.reset();  
for (int i = 0; i < repeatCount; i++)  
{  
    akumaslab::time::PreciseTimer::wait(interval);  
    cout << getCurrentTime() << "/" << getNewTime() << " used " << unit.getPreciseElapsedTime() << " ms" << endl;  
    unit.reset();  
}  

 

2、精确时刻获取

[cpp]
view plaincopy

#pragma once  
  
#include <sys/timeb.h>  
#include <time.h>  
#include <Windows.h>  
#include <akumaslab/system/singleton.hpp>  
  
namespace akumaslab{  
    namespace time{  
        struct HighResolutionTime  
        {  
            int year;  
            int month;  
            int day;  
            int hour;  
            int min;  
            int second;  
            int millisecond;  
        };  
        class CurrentTimeProvider  
        {  
        public:  
            CurrentTimeProvider():highResolutionAvailable(false), countPerMilliSecond(0), beginCount(0)  
            {  
                static LARGE_INTEGER systemFrequency;  
                if(0 != QueryPerformanceFrequency(&systemFrequency))  
                {  
                    highResolutionAvailable = true;  
                    countPerMilliSecond = systemFrequency.QuadPart/1000;  
                    _timeb tb;  
                    _ftime_s(&tb);  
                    unsigned short currentMilli = tb.millitm;  
                    LARGE_INTEGER now;  
                    QueryPerformanceCounter(&now);  
                    beginCount = now.QuadPart - (currentMilli*countPerMilliSecond);  
                }  
            };  
            bool getCurrentTime(HighResolutionTime& _time)  
            {  
                time_t tt;  
                ::time(&tt);  
                tm now;  
                localtime_s(&now, &tt);  
                _time.year = now.tm_year + 1900;  
                _time.month = now.tm_mon + 1;  
                _time.day = now.tm_mday + 1;  
                _time.hour = now.tm_hour;  
                _time.min = now.tm_min;  
                _time.second = now.tm_sec;  
                if (!highResolutionAvailable)  
                {  
                    _time.millisecond = 0;  
                }  
                else{  
                    LARGE_INTEGER qfc;  
                    QueryPerformanceCounter(&qfc);  
                    _time.millisecond = (int)((qfc.QuadPart - beginCount)/countPerMilliSecond)%1000;  
                }  
                return true;  
            }  
        private:  
            bool highResolutionAvailable;  
            LONGLONG countPerMilliSecond;  
            LONGLONG beginCount;  
        };  
        class CurrentTime  
        {  
        public:  
            static bool get(HighResolutionTime& _time)  
            {  
                return akumaslab::system::Singleton< CurrentTimeProvider >::getRef().getCurrentTime(_time);  
            }  
        };  
    }  
}  

DEMO:

[cpp]
view plaincopy

HighResolutionTime time;  
CurrentTime::get(time);  
const int size = 20;  
char buf[size] = {0};  
_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能够尽快提供真正的高精度时间管理技术





顶 1 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: