首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

Windows 多媒體定時器實現毫秒級精確定時(CMMTimer 類封裝)

     編輯:關於VC++

為什麼需要毫秒定時器?

  • 標準 SetTimer 精度不足:Windows 消息隊列機制導致最小間隔 ~10-15ms,且受 UI 線程阻塞影響
  • 無窗口類無法使用:CWnd::SetTimer 依賴窗口消息泵,非 GUI 類(如數據處理、網絡庫)無法直接使用
  • 實時性需求:音視頻播放、遊戲循環、工控采集、高頻數據採樣需要 1ms 級精度

核心 API:WinMM 多媒體定時器

API用途
timeGetDevCaps查詢系統定時器分辨率範圍
timeBeginPeriod設置期望分辨率(如 1ms)
timeEndPeriod釋放分辨率資源
timeSetEvent創建定時器,指定回調函數、週期、模式
timeKillEvent銷毀定時器

CMMTimer 類設計

UML 結構

CMMTimer UML 類圖

  • CTimerListener:抽象回調接口,定義 OnMMTimer(UINT timerID)
  • CMMTimers:管理多個定時器,內部維護 MAP<UINT, CTimerListener*>
  • 使用類:繼承 CTimerListener,實現 OnMMTimer

核心接口

class CMMTimers {
public:
    // 啟動定時器
    // period: 週期(ms), pListener: 回調對象, nTimerID: ID, oneShot: 單次/週期
    bool startTimer(UINT period, CTimerListener* pListener = NULL, int nTimerID = 1, bool oneShot = FALSE);
    
    // 停止指定定時器
    bool stopTimer(int nTimerID);
    
    // 停止所有定時器
    void stopTimer();
    
    // 內部回調入口(static)
    static void CALLBACK internalTimerProc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2);
    
    // 派生類可選重寫
    virtual void timerProc(UINT nTimerID);
};

關鍵實現細節

// 靜態回調函數 - 系統調用
void CALLBACK CMMTimers::internalTimerProc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {
    CMMTimers* pThis = (CMMTimers*)dwUser;  // dwUser 傳遞 this 指針
    if (pThis) pThis->timerProc(uTimerID);
}

// 實例方法 - 分發給具體監聽器
void CMMTimers::timerProc(UINT nTimerID) {
    auto it = m_mapListeners.find(nTimerID);
    if (it != m_mapListeners.end() && it->second) {
        it->second->OnMMTimer(nTimerID);
    }
}

// 啟動定時器
bool CMMTimers::startTimer(UINT period, CTimerListener* pListener, int nTimerID, bool oneShot) {
    TIMECAPS tc;
    timeGetDevCaps(&tc, sizeof(TIMECAPS));
    period = max(period, tc.wPeriodMin);  // 確保不低於系統最小分辨率
    timeBeginPeriod(period);
    
    UINT flags = oneShot ? TIME_ONESHOT : TIME_PERIODIC;
    MMRESULT id = timeSetEvent(period, 0, internalTimerProc, (DWORD_PTR)this, flags);
    if (id == 0) return false;
    
    m_mapTimers[nTimerID] = id;
    if (pListener) m_mapListeners[nTimerID] = pListener;
    return true;
}

使用示例

class CTestMMTimerDlg : public CDialog, public CTimerListener {
public:
    CMMTimers m_timer;
    
    void OnBtnStart() {
        m_timer.startTimer(1, this, 1001);  // 1ms 週期定時器
    }
    
    void OnBtnStop() {
        m_timer.stopTimer(1001);
    }
    
    // 實現回調接口
    virtual void OnMMTimer(UINT nTimerID) override {
        if (nTimerID == 1001) {
            // 毫秒級任務:數據采集、波形刷新、協議心跳等
            UpdateWaveform();
        }
    }
};

優缺點對比

特性CWnd::SetTimer多媒體定時器
精度~10-15ms1ms+ (視系統而定)
依賴窗口是否
回調線程UI 線程系統高優先級線程
UI 操作安全安全需 PostMessage 回 UI 線程
資源消耗低較高 (提高系統時鐘中斷頻率)

注意事項與最佳實踐

  1. 成對調用:timeBeginPeriod/timeEndPeriod 必須成對,否則影響系統電源管理
  2. 回調線程安全:回調在系統線程執行,嚴禁直接操作 UI,必須通過 PostMessage 或線程安全隊列傳遞給 UI 線程
  3. 避免長耗時操作:回調中執行耗時邏輯會導致定時器漂移甚至系統卡頓
  4. 精度期望管理:Windows 非實時系統,1ms 定時器實際抖動可能 ±0.5-1ms,極端情況下更大
  5. 現代替代方案:Windows 10 1703+ 可用 CreateTimerQueueTimer + SetTimerResolution;高精度場景考慮 QueryPerformanceCounter 忙等待或專用實時線程

運行效果演示

毫秒定時器運行截圖

界面顯示定時器觸發計數,驗證 1ms 週期準確性

總結

封裝 CMMTimers 類解決了「無窗口類需要高精度定時」的痛點,結合 CTimerListener 接口實現了靈活的回調機制。雖然多媒體定時器提高了系統時鐘中斷頻率(增加功耗),但在工業控制、多媒體處理、高頻交易等場景中不可或缺。代碼結構清晰,體現了設計模式中「觀察者模式」與「適配器模式」的實踐。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved