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

C++ 毫秒級定時器類 LRTimer:基於 Windows 可等待定時器與 APC 異步回調

     編輯:關於C++

設計目標

  • 最小間隔 1ms,精度優於 WM_TIMER(~10-15ms)。
  • 獨立線程運行,不阻塞主線程。
  • 支持任意回調函數 void callback(void*)。
  • 動態修改間隔、啟動/停止控制。
  • 跨編譯器兼容(MSVC、MinGW)。

核心架構

class LRTimer {
public:
    LRTimer(DWORD intervalMs = 1000);
    ~LRTimer();

    void start();                    // 用構造/設置的間隔啟動
    void start(DWORD intervalMs);    // 新間隔啟動
    void stop();                     // 停止
    void setInterval(DWORD ms);      // 設置間隔
    DWORD getInterval() const;
    void setCallback(void(*cb)(void*), void* param);  // 設置回調
    bool isRunning() const;

private:
    static DWORD WINAPI ThreadEntry(void* pThis);      // 線程入口
    DWORD ThreadProc();                                // 實例線程邏輯
    static void CALLBACK ApcEntry(void* pThis, DWORD, DWORD); // APC 入口
    void ApcProc();                                    // 實例 APC 邏輯

    DWORD interval_;               // 間隔(ms)
    void(*callback_)(void*);       // 回調函數
    void* cbParam_;                // 回調參數
    bool running_;                 // 運行標誌
    HANDLE thread_;                // 線程句柄
    DWORD threadId_;               // 線程ID
};

Windows 可等待定時器機制

  1. 創建:CreateWaitableTimer 創建內核定時器對象。
  2. 設置:SetWaitableTimer 指定首次觸發時間(相對 100ns 為單位)、週期間隔、APC 回調函數、參數。
  3. 等待:線程調用 SleepEx(1, TRUE) 進入可提醒等待狀態,APC 排隊執行。
  4. APC 執行:定時器觸發 → 內核排隊 APC → 線程下次可提醒等待返回 → 執行 ApcProc → 調用用戶回調。
  5. 清理:CancelWaitableTimer、CloseHandle。

關鍵實現細節

線程入口適配器

static DWORD WINAPI ThreadEntry(void* pThis) {
    return static_cast(pThis)->ThreadProc();
}

線程主循環

DWORD LRTimer::ThreadProc() {
    HANDLE timer = CreateWaitableTimer(nullptr, FALSE, name);
    LARGE_INTEGER due; due.QuadPart = -interval_ * 10000;  // 100ns 為單位,負數=相對時間
    SetWaitableTimer(timer, &due, interval_, ApcEntry, this, FALSE);

    while (running_)
        SleepEx(1, TRUE);  // 1ms 精度,TRUE=可提醒

    CancelWaitableTimer(timer);
    CloseHandle(timer);
    return 0;
}

APC 回調適配器

static void CALLBACK ApcEntry(void* pThis, DWORD, DWORD) {
    static_cast(pThis)->ApcProc();
}

void LRTimer::ApcProc() {
    if (callback_) callback_(cbParam_);
}

CRT 線程兼容啟動

#ifndef _INC_CRTDEFS
    thread_ = CreateThread(nullptr, 0, ThreadEntry, this, 0, &threadId_);
#else
    thread_ = (HANDLE)_beginthreadex(nullptr, 0,
        reinterpret_cast(ThreadEntry),
        this, 0, reinterpret_cast(&threadId_));
#endif

使用示例

void myCallback(void* data) {
    static int cnt = 0;
    const char* sp = "|/-\\";
    printf("\r%c", sp[cnt++ % 4]);  // 旋轉光標動畫
}

int main() {
    LRTimer timer;
    timer.setCallback(myCallback, nullptr);
    timer.setInterval(50);      // 50ms = 20Hz
    timer.start();

    getchar();  // 運行一段時間
    timer.stop();

    getchar();  // 驗證已停
    timer.start(200);  // 修改間隔重啟
    getchar();
    timer.stop();
    return 0;
}

優缺點與適用場景

優點缺點
1ms 精度,無消息泵依賴僅限 Windows 平台
APC 異步回調,不阻塞定時器線程回調執行於定時器線程,不可長時間阻塞
動態調整間隔、啟停靈活高頻回調(>1kHz)APC 佇列可能擁堵
輕量級,無 MFC/ATL 依賴需手動管理回調線程安全

現代替代方案

  • C++20 std::jthread + std::chrono:跨平台,協程友好。
  • Boost.Asio steady_timer:Proactor 模式,高性能網絡/定時統一。
  • Windows 線程池定時器:CreateThreadpoolTimer,系統管理線程,更省資源。
  • 多媒體定時器:timeSetEvent,1ms 精度,回調在系統線程,已棄用建議遷移。

遷移建議

  1. 新項目優先用 C++20/Boost.Asio 實現跨平台定時器抽象層。
  2. 現有 Windows 專用代碼可保留 LRTimer,封裝接口便於未來替換後端。
  3. 回調中耗時操作必須派發到工作線程池,定時器線程僅負責調度。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved