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

C++Builder 多媒體定時器:timeSetEvent 高精度週期性回調實作

     編輯:關於C++

為何不用 VCL TTimer?

  • TTimer 精度約 10-15 ms(受訊息佇列影響),且最小間隔受限於系統時鐘節拍。
  • 多媒體定時器 (timeSetEvent) 可達 **1 ms** 級精度,在獨立執行緒回調,適合音訊、採集、控制迴路。

API 簽名速記

MMRESULT timeSetEvent(
    UINT uDelay,           // 週期 (ms)
    UINT uResolution,      // 解析度 (0=系統預設)
    LPTIMECALLBACK lpCallback, // 回調函數
    DWORD dwUser,          // 使用者資料
    UINT fuEvent           // 旗標
);

void timeKillEvent(MMRESULT uTimerID);

常用旗標:TIME_PERIODIC (週期性) | TIME_CALLBACK_FUNCTION (函數回調)。

最小化完整範例

Unit1.h

#ifndef Unit1H
#define Unit1H
#include 
#include 
#include 
#include 

class TForm1 : public TForm {
__published:
    TButton *Button1;
    TLabel  *Label1;
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
private:
public:
    __fastcall TForm1(TComponent* Owner);
    static void CALLBACK TimeProc(UINT uID, UINT uMsg,
                                  DWORD dwUser, DWORD dw1, DWORD dw2);
    MMRESULT TimerID;
};
extern PACKAGE TForm1 *Form1;
#endif

Unit1.cpp

#include 
#include 
#pragma hdrstop
#include "Unit1.h"
#pragma comment(lib, "winmm.lib")

#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
    TimerID = 0;
}

void CALLBACK TForm1::TimeProc(UINT, UINT, DWORD, DWORD, DWORD) {
    // 注意:回調在獨立執行緒,直接操作 VCL 控件不安全!
    // 這裡僅為示範,實務請用 PostMessage/Synchronize/佇列傳遞主執行緒
    Form1->Label1->Caption = FormatDateTime("hh:nn:ss.zzz", Now());
}

void __fastcall TForm1::Button1Click(TObject *Sender) {
    TimerID = timeSetEvent(1000, 0, (LPTIMECALLBACK)TimeProc, 0,
                           TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
    if (TimerID == 0) ShowMessage("建立定時器失敗");
}

void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose) {
    if (TimerID) { timeKillEvent(TimerID); TimerID = 0; }
}

執行緒安全:跨執行緒更新 UI 的三種正確姿勢

方法適用場景代碼片段
PostMessage簡單通知、低頻PostMessage(Handle, WM_USER+1, 0, 0); → 主執行緒 WndProc 處理
TThread::Synchronize需執行複雜 VCL 邏輯Synchronize([&]{ Label1->Caption = ...; }); (C++11 Lambda)
無鎖佇列 + Timer 輪詢高頻資料流 (音訊/感測器)回調 queue.push(data);主執行緒 Timer while(queue.pop(d)) updateUI(d);

精度與解析度調校

  • timeBeginPeriod(1):將系統時鐘解析度提升至 1 ms(配合 timeEndPeriod 成對使用)。
  • 若需 < 1 ms 精度,請改用 高效能計數器 (QueryPerformanceCounter) + 忙等待或多媒體定時器 TIME_KILL_SYNCHRONOUS 旗標(Vista+)。

常見錯誤

  1. 回調中直接操作 VCL → 競態條件、AV、介面凍結。
  2. 忘記 timeKillEvent → 定時器洩漏,卸載 DLL 時崩潰。
  3. 解析度設太高 → 系統整體功耗上升,請最小化使用 timeBeginPeriod。

結語

timeSetEvent 是 Windows 多媒體定時器的經典入口,配合正確的跨執行緒 UI 更新模式,能滿足多數毫秒級定時需求。現代專案若需更高精度或跨平台,建議改用 C++11 std::chrono + std::thread 或 Boost.Asio steady_timer。

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