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

Delphi 多線程程式設計 (17):WaitableTimer 等待定時器 - APC 回調函數時間參數解析與指標傳遞實戰

     編輯:Delphi

WaitableTimer 核心優勢在於**非同步程序呼叫 (APC)** 機制:定時器到期時將回調函數排入線程 APC 隊列,線程處於可喚醒等待狀態 (SleepEx, WaitForSingleObjectEx 等) 時立即執行,實現高精度、低延遲的定時任務。本文深入解析 APC 回調的時間參數合成與指標參數傳遞實戰。

APC 回調函數標準原型

procedure TimerAPCProc(
  lpArgToCompletionRoutine: Pointer;  // SetWaitableTimer 第 5 參數傳入
  dwTimerLowValue: DWORD;             // 觸發時間低 32 位 (UTC FILETIME)
  dwTimerHighValue: DWORD             // 觸發時間高 32 位 (UTC FILETIME)
); stdcall;

時間參數合成:雙 DWORD 還原 TFileTime

var
  UTCFileTime, LocalFileTime: TFileTime;
  SystemTime: TSystemTime;
  DateTime: TDateTime;
begin
  { 1. 合成 UTC FILETIME }
  UTCFileTime.dwLowDateTime  := dwTimerLowValue;
  UTCFileTime.dwHighDateTime := dwTimerHighValue;
  
  { 2. UTC -> 本地時間 }
  FileTimeToLocalFileTime(UTCFileTime, LocalFileTime);
  
  { 3. FILETIME -> SYSTEMTIME }
  FileTimeToSystemTime(LocalFileTime, SystemTime);
  
  { 4. SYSTEMTIME -> TDateTime }
  DateTime := SystemTimeToDateTime(SystemTime);
  
  { 使用 DateTime... }
end;

範例一:週期性顯示精確觸發時間

unit Unit1;

interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm)
  Button1, Button2: TButton;
  procedure Button1Click(Sender: TObject);
  procedure Button2Click(Sender: TObject);
  procedure FormDestroy(Sender: TObject);
end;
var Form1: TForm1;
implementation {$R *.dfm}

var hTimer: THandle;

procedure TimerAPCProc(lpArg: Pointer; dwLow, dwHigh: DWORD); stdcall;
var UTC, Local: TFileTime; ST: TSystemTime; DT: TDateTime;
begin
  UTC.dwLowDateTime := dwLow;  UTC.dwHighDateTime := dwHigh;
  FileTimeToLocalFileTime(UTC, Local);
  FileTimeToSystemTime(Local, ST);
  DT := SystemTimeToDateTime(ST);
  Form1.Caption := DateTimeToStr(DT);  // 顯示精確觸發時間
  SleepEx(INFINITE, True);  // 保持可喚醒等待
end;

function MyThreadFun(p: Pointer): Integer; stdcall;
var DueTime: Int64;
begin
  DueTime := 0;  // 立即開始
  if SetWaitableTimer(hTimer, DueTime, 1000, @TimerAPCProc, nil, False) then
    SleepEx(INFINITE, True);
  Result := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var ID: DWORD;
begin
  if hTimer = 0 then hTimer := CreateWaitableTimer(nil, True, nil);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ID);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  CancelWaitableTimer(hTimer);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  CloseHandle(hTimer);
end;
end.

範例二:指標參數傳遞滑鼠點擊座標

SetWaitableTimer 第 5 參數 lpArgToCompletionRoutine 可傳遞任意指標,APC 回調第一參數接收。此例傳遞 TPoint 記錄點擊位置並在該處繪製時間。

var
  hTimer: THandle;
  pt: TPoint;  // 全域座標暫存

procedure TimerAPCProc(lpArg: Pointer; dwLow, dwHigh: DWORD); stdcall;
var UTC, Local: TFileTime; ST: TSystemTime; DT: TDateTime; pt2: TPoint;
begin
  UTC.dwLowDateTime := dwLow;  UTC.dwHighDateTime := dwHigh;
  FileTimeToLocalFileTime(UTC, Local);
  FileTimeToSystemTime(Local, ST);
  DT := SystemTimeToDateTime(ST);
  pt2 := PPoint(lpArg)^;  // 解引用接收座標
  Form1.Canvas.Lock;
  Form1.Canvas.TextOut(pt2.X, pt2.Y, DateTimeToStr(DT));
  Form1.Canvas.Unlock;
  SleepEx(INFINITE, True);
end;

function MyThreadFun(p: Pointer): Integer; stdcall;
var DueTime: Int64;
begin
  DueTime := 0;
  { 傳遞 @pt 指標給 APC }
  if SetWaitableTimer(hTimer, DueTime, 1000, @TimerAPCProc, @pt, False) then
    SleepEx(INFINITE, True);
  Result := 0;
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var ID: DWORD;
begin
  pt := Point(X, Y);  // 記錄點擊座標
  if hTimer = 0 then hTimer := CreateWaitableTimer(nil, True, nil);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ID);
end;

DFM 定義 (範例二)

object Form1: TForm1
  OnMouseDown = FormMouseDown
  ...
end

執行效果展示

週期性顯示系統時間

滑鼠點擊處每秒繪製一次時間戳

APC 機制核心原理

  1. 每線程擁有獨立 **APC 隊列** (非訊息隊列)。
  2. SetWaitableTimer 到期 → 將 APC 回調排入**目標線程** APC 隊列。
  3. 線程必須處於 **可喚醒等待狀態** (SleepEx(..., True), WaitForSingleObjectEx(..., True) 等) 才會處理 APC。
  4. APC 優先級高於訊息隊列,執行完畢返回繼續等待。

關鍵等待函數對照 (支援 APC 進隊)

函數特點
SleepEx(ms, True)最簡單,單參數毫秒 + 可喚醒旗標
WaitForSingleObjectEx(h, ms, True)等待單一物件 + APC
WaitForMultipleObjectsEx(n, handles, waitAll, ms, True)等待多物件 + APC
MsgWaitForMultipleObjectsEx(...)等待物件+訊息+APC (GUI 線程常用)
SignalObjectAndWait(hSignal, hWait, ms, True)發信號+等待 原子操作 + APC

實務開發提醒

  • 同一線程:SetWaitableTimer 與 SleepEx **必須在同一線程**,否則 APC 無法進隊。
  • 指標有效性:傳遞的指標指向的記憶體必須在 APC 執行時仍有效 (全域/堆/線程本地存儲),避免棧上變數失效。
  • 取消定時器:CancelWaitableTimer(hTimer) 可隨時停止週期觸發。
  • 精度優勢:WaitableTimer 精度達 0.1ms (100ns 單位),遠優於 TTimer (WM_TIMER 訊息佇列最低優先級,誤差常 > 10ms)。
  • 跨進程:具名 CreateWaitableTimer 支援跨進程同步。

掌握 APC 回調與指標傳遞,即能發揮 WaitableTimer 超越 TTimer 的高精度、非阻塞、上下文感知定時能力。

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