高精度定时器——windows多媒体定时器、linux posix timer,封装使用
周期性地执行一段代码,while死循环+sleep是一种方式,但是精度在10ms以上。
while死循环里如果没有sleep,那么会单独占用1个CPU核(即CPU占用率很高)。
sleep即使设置为1ms(见下方代码),经示波器测试发现周期至少在10ms以上。
Sleep(1); //Windows api WaitForSingleObject(hThread, 1); //Windows api std::this_thread::sleep_for(std::chrono::milliseconds(1)); //c++ api
因为:sleep 1表示暂停它至少1毫秒。即告诉操作系统将线程放入睡眠队列中,一旦过了1毫秒,就应该认为该线程有资格再次执行。但这仍然取决于操作系统是否能调度您的线程,这可能需要另外10ms (或更多,或更少,取决于各种因素)。
高精度定时器可以精确到1ms,以windows多媒体定时器为例。timeSetEvent()产生一个独立的线程,在一定的中断次数后直接调用回调函数,不等待应用程序的消息队列为空(即不依赖消息机制),保证了实时响应。
#include<iostream> //windows高精度定时器必须引入如下两行 #include<Windows.h> #pragma comment(lib,"winmm.lib") LARGE_INTEGER nEndTime, nBeginTime, nFreq; double time; void CALLBACK CallBackFunc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) { QueryPerformanceCounter(&nEndTime); time = (double)(nEndTime.QuadPart - nBeginTime.QuadPart) * 1000 / (double)nFreq.QuadPart;//ms(开始-停止)/频率即为秒数,精确到小数点后6位 printf("当前时刻(ms): %f \n\n", time); } int main() { QueryPerformanceFrequency(&nFreq); QueryPerformanceCounter(&nBeginTime);//获取开始时刻计数值 timeSetEvent(1, 1, CallBackFunc, (DWORD)NULL, TIME_PERIODIC); //每1ms触发一次 Sleep(10000); return 0; }
回调函数的参数含义参考LPTIMECALLBACK function pointer (Windows) | Microsoft Learn
timeSetEvent()的参数含义参考timeSetEvent function (Windows) | Microsoft Learn
为了方便跨平台,封装为1个头文件,使用时引入即可。
HighResolutionTimer.hpp
#ifndef HIGHRESOLUTIONTIMER_H #define HIGHRESOLUTIONTIMER_H #if defined(_MSC_VER_) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) #ifndef OS_IS_WIN #define OS_IS_WIN #endif #else #ifndef OS_IS_LNIX #define OS_IS_LNIX #endif #endif #if defined (OS_IS_WIN) #include <Windows.h> #pragma comment(lib,"winmm.lib") #else #include <stdlib.h> #include <signal.h> #include <time.h> #include <sys/time.h> #include <unistd.h> #endif #include <iostream> #include <functional> /// 定时器回调函数 using OnTimeoutFunc = std::function<void()>; /// 定时器类型 enum TIMER_TYPE { TT_PERIOD=1, //周期执行 TT_ONCE=0 //单次触发 }; class HighResolutionTimer { public: inline HighResolutionTimer(); inline ~HighResolutionTimer(); inline int init(const TIMER_TYPE timer_type, OnTimeoutFunc cb); inline int begin(const unsigned int interval_ms); inline int end(); private: int m_timer_type = TT_ONCE; OnTimeoutFunc m_timeout_func = nullptr; #ifdef OS_IS_WIN inline static void CALLBACK TimeProc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); unsigned int m_timer_id = 0; unsigned int m_timer_precision = 1; //系统所支持的定时器最高精度(ms) #else inline static void handler(int,siginfo_t* si,void*); timer_t timerid = nullptr; struct sigevent sev; struct sigaction sa; struct itimerspec its; #endif }; HighResolutionTimer::HighResolutionTimer() { #ifdef OS_IS_WIN TIMECAPS tc; if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { printf("call [timeGetDevCaps] function error"); } else { m_timer_precision = tc.wPeriodMin; //定时器所支持的最高精度 timeBeginPeriod(m_timer_precision); } #else sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = handler; sigemptyset(&sa.sa_mask); if (sigaction(SIGRTMIN, &sa, NULL) == -1) printf("Could not create signal handler"); sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGRTMIN; sev.sigev_value.sival_ptr = this; if (timer_create(CLOCKID, &sev, &timerid) == -1) printf("Could not create timer"); #endif } HighResolutionTimer::~HighResolutionTimer() { end(); } int HighResolutionTimer::init(const TIMER_TYPE timer_type, OnTimeoutFunc cb) { m_timer_type = timer_type; m_timeout_func = std::move(cb); return 0; } int HighResolutionTimer::begin(const unsigned int interval_ms) { #ifdef OS_IS_WIN end(); //避免多次begin if (NULL == (m_timer_id = timeSetEvent(interval_ms, NULL, TimeProc, (DWORD_PTR)this, m_timer_type))) { printf("failed to create timer"); return -1; } #else switch (m_timer_type) { case (TT_PERIOD): its.it_value.tv_sec = interval_ms / 1000; its.it_value.tv_nsec = (interval_ms % 1000) * 1000000; its.it_interval.tv_sec = interval_ms / 1000; its.it_interval.tv_nsec = (interval_ms % 1000) * 1000000; break; case (TT_ONCE): its.it_value.tv_sec = interval_ms / 1000; its.it_value.tv_nsec = (interval_ms % 1000) * 1000000; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; break; default: break; } if (-1 == timer_settime(timerid, 0, &its, NULL)) { printf("call [timer_settime] function error"); return -1; } #endif return 0; } int HighResolutionTimer::end() { #ifdef OS_IS_WIN if (m_timer_id!=0) { timeKillEvent(m_timer_id); m_timer_id = 0; timeEndPeriod(m_timer_precision); } #else if (timerid) { struct itimerspec itsnew; itsnew.it_value.tv_sec = 0; itsnew.it_value.tv_nsec = 0; itsnew.it_interval.tv_sec = 0; itsnew.it_interval.tv_nsec = 0; timer_settime(timerid, 0, &itsnew, &its); timer_delete(timerid); signal(sev.sigev_signo, SIG_IGN); timerid = nullptr; } #endif return 0; } #ifdef OS_IS_WIN void CALLBACK HighResolutionTimer::TimeProc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) { HighResolutionTimer* pFunc = (HighResolutionTimer*)(dwUser); if (pFunc) { pFunc->m_timeout_func(); } } #else void HighResolutionTimer::handler(int, siginfo_t* si, void*) { HighResolutionTimer* pFunc = reinterpret_cast<HighResolutionTimer*>(si->si_value.sival_ptr); if (pFunc) { pFunc->m_timeout_func(); } } #endif #endif
使用
#include"HighResolutionTimer.hpp" LARGE_INTEGER nEndTime, nBeginTime, nFreq; double time; void timeout() { QueryPerformanceCounter(&nEndTime); time = (double)(nEndTime.QuadPart - nBeginTime.QuadPart) * 1000 / (double)nFreq.QuadPart;//ms(开始-停止)/频率即为秒数,精确到小数点后6位 printf("当前时刻(ms): %f \n\n", time); } int main() { QueryPerformanceFrequency(&nFreq); QueryPerformanceCounter(&nBeginTime);//获取开始时刻计数值 HighResolutionTimer hTimer; hTimer.init(TT_PERIOD, timeout); //周期执行 hTimer.begin(1); //周期为1ms Sleep(1000000); return 0; }