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

C++Builder 高精度計時:timeSetEvent 多媒體定時器實戰

     編輯:VC 教學

為何標準 Timer 不夠用?

  • TTimer (WM_TIMER) 精度 ~55ms,優先級極低,訊息佇列擁塞時嚴重延遲。
  • 多媒體定時器 (timeSetEvent) 精度 **1ms 級**,獨立執行緒回呼,適合即時控制、音訊同步、高頻採樣。

核心 API (mmsystem.h / winmm.lib)


#include 
#pragma comment(lib, "winmm.lib")

// 1. 取得系統啟動後毫秒數
DWORD timeGetTime();  // 回繞週期 ~49.7 天

// 2. 建立定時器事件
MMRESULT timeSetEvent(
    UINT uDelay,           // 週期 (ms)
    UINT uResolution,      // 解析度 (ms),越小越精確,建議 1
    LPTIMECALLBACK lpTimeProc, // 回呼函式
    DWORD dwUser,          // 使用者資料傳遞給回呼
    UINT fuEvent           // TYPE: TIME_ONESHOT | TIME_PERIODIC
); // 成功返回 Timer ID,失敗返回 0

// 3. 刪除定時器
MMRESULT timeKillEvent(UINT uTimerID);

// 4. 回呼函式原型 (必須 __stdcall / CALLBACK)
void CALLBACK TimeProc(
    UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2
);

回呼函式限制(極重要)

回呼在**多媒體定時器執行緒**中執行,**只能呼叫極少量 API**:

  • PostMessage, timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent
  • midiOutShortMsg, midiOutLongMsg, OutputDebugString
  • 嚴禁:任何 UI 操作、檔案 I/O、記憶體分配、鎖、重量級 API、長時間運算。

**正確模式**:回呼僅 PostMessage(hwnd, WM_MY_TIMER, 0, 0) 通知主執行緒,主執行緒訊息迴路處理實際邏輯。

C++Builder 完整範例


// Unit1.h
class TForm1 : public TForm {
__published:
    TButton *BtnStart, *BtnStop;
    TLabel *LblCount;
    void __fastcall BtnStartClick(TObject* Sender);
    void __fastcall BtnStopClick(TObject* Sender);
private:
    UINT timerId;
    static void CALLBACK TimerProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD, DWORD);
    void __fastcall HandleTimer();  // 主執行緒處理
    BEGIN_MESSAGE_MAP
        VCL_MESSAGE_HANDLER(WM_USER+1, TMessage, OnTimerMsg)
    END_MESSAGE_MAP(TForm)
public:
    __fastcall TForm1(TComponent* Owner);
};

// Unit1.cpp
#define WM_TIMER_NOTIFY (WM_USER + 1)

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner), timerId(0) {}

void __fastcall TForm1::BtnStartClick(TObject* Sender) {
    if (timerId) return;
    timerId = timeSetEvent(10, 1, TimerProc, (DWORD)this, TIME_PERIODIC);
    if (!timerId) ShowMessage("建立定時器失敗");
}

void __fastcall TForm1::BtnStopClick(TObject* Sender) {
    if (timerId) { timeKillEvent(timerId); timerId = 0; }
}

void CALLBACK TForm1::TimerProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD, DWORD) {
    TForm1* self = (TForm1*)dwUser;
    PostMessage(self->Handle, WM_TIMER_NOTIFY, 0, 0);  // 唯一允許的 UI 互動
}

void __fastcall TForm1::OnTimerMsg(TMessage& Msg) {
    HandleTimer();  // 主執行緒安全執行 UI/邏輯
}

void __fastcall TForm1::HandleTimer() {
    static int count = 0;
    LblCount->Caption = "Count: " + IntToStr(++count);
}

精度與資源權衡

  • uResolution = 1 最高精度,但系統會更頻繁喚醒 CPU,增加功耗、減少電池壽命。
  • 滿足需求前提下,**盡量增大 uResolution**(如 5ms、10ms)。
  • 定時器不再需要時**立即 timeKillEvent** 釋放資源。

現代替代方案

總結

多媒體定時器 timeSetEvent 是 Win32 經典高精度週期性回呼方案。**核心鐵律:回呼極度輕量,僅 PostMessage 通知主執行緒**。精度 1ms 可達,但需權衡 CPU 功耗。現代專案優先考慮 Timer Queue Timer 或 C++20 標準庫方案,獲得更好跨平台性與資源管理。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方案精度適用場景備註
CreateTimerQueueTimer (Thread Pool)~1ms一般背景週期性任務Win2K+,自動執行緒池管理
SetWaitableTimer + WaitForSingleObject~0.5ms需精確喚醒的同步等待可關聯非訊號狀態
QueryPerformanceCounter + 忙等待/睡眠修正亞微秒極高精度測量/同步CPU 佔用高,僅測量或極短間隔
C++20 std::chrono + std::jthread~ms跨平台現代代碼配合 sleep_until 迴圈