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

Delphi 多線程程式設計 (15):WaitableTimer 等待定時器 - 起始時間參數詳解 (相對/絕對/零值)

     編輯:Delphi

SetWaitableTimer 第二參數 lpDueTime (類型 Int64 / TLargeInteger) 控制定時器**首次觸發時間**,支援三種模式:零值 (立即)、負值 (相對時間)、正值 (絕對時間)。本文詳細解析三種模式的賦值計算與實戰應用。

三種時間模式對照

模式數值範圍含義單位典型用途
立即0設定後立即觸發第一次-即時啟動週期任務
相對時間< 0從當前時間起延遲指定間隔100 奈秒 (0.1μs)延遲啟動、倒數計時
絕對時間> 0指定 UTC FILETIME 絕對時間點100 奈秒 (UTC)定時啟動、排程任務

時間單位換算基礎

1 秒 = 1,000 毫秒 (ms)
    = 1,000,000 微秒 (μs)
    = 1,000,000,000 奈秒 (ns)
    = 10,000,000 * 100 奈秒單位  ← FILETIME 基礎單位

範例一:相對時間 (負值) - 3 秒後啟動三線程

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

var f: Integer; hWaitableTimer: THandle;

function MyThreadFun(p: Pointer): DWORD; stdcall;
var i, y: Integer;
begin
  Inc(f); y := 20 * f;
  if WaitForSingleObject(hWaitableTimer, INFINITE) = WAIT_OBJECT_0 then
    for i := 0 to 1000 do
    begin
      Form1.Canvas.Lock; Form1.Canvas.TextOut(20, y, IntToStr(i)); Form1.Canvas.Unlock;
      Sleep(1);
    end;
  Result := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var ThreadID: DWORD; DueTime: Int64;
begin
  hWaitableTimer := CreateWaitableTimer(nil, True, nil);  // True = 手動重置,喚醒所有等待線程
  DueTime := -3 * 10000000;  // -30,000,000 = -3 秒 (100ns 單位)
  SetWaitableTimer(hWaitableTimer, DueTime, 0, nil, nil, False);
  Repaint; f := 0;
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
end;

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

範例二:絕對時間 (正值) - 指定 2009-02-18 13:10:05 啟動

procedure TForm1.Button1Click(Sender: TObject);
const strTime = '2009-02-18 13:10:05';
var ThreadID: DWORD; DueTime: Int64;
    st: TSystemTime; ft, utc: TFileTime; dt: TDateTime;
begin
  { 字串 -> TDateTime -> TSystemTime -> FILETIME (本地) -> FILETIME (UTC) }
  DateTimeToSystemTime(StrToDateTime(strTime), st);
  SystemTimeToFileTime(st, ft);           // 本地 FILETIME
  LocalFileTimeToFileTime(ft, utc);       // 轉 UTC FILETIME (SetWaitableTimer 需 UTC)
  DueTime := Int64(utc);                  // 整數轉換
  
  hWaitableTimer := CreateWaitableTimer(nil, True, nil);
  SetWaitableTimer(hWaitableTimer, DueTime, 0, nil, nil, False);
  Repaint; f := 0;
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
end;

時間轉換鏈路圖解


字串 '2009-02-18 13:10:05'
       ↓ StrToDateTime
TDateTime (Double)
       ↓ DateTimeToSystemTime
TSystemTime (Year/Month/Day/Hour/Min/Sec/MSec)
       ↓ SystemTimeToFileTime
TFileTime (本地時間, 100ns 單位)
       ↓ LocalFileTimeToFileTime
TFileTime (UTC 時間)  ← SetWaitableTimer 要求 UTC
       ↓ Int64 強制轉型
Int64 (DueTime)

關鍵結構體定義

type
  TFileTime = record
    dwLowDateTime: DWORD;
    dwHighDateTime: DWORD;
  end;  // 相當於 Int64

  TSystemTime = record
    wYear, wMonth, wDayOfWeek, wDay: Word;
    wHour, wMinute, wSecond, wMilliseconds: Word;
  end;

  TDateTime = type Double;

執行效果展示

按鈕點擊 3 秒後三線程同時開始輸出

常見錯誤與避坑指南

錯誤後果正確做法
直接傳遞常數 SetWaitableTimer(h, 0, ...)編譯錯誤: lpDueTime 為 var 參數用變數 DueTime: Int64; DueTime := 0;
相對時間用正值被視為絕對時間 (通常為極遠未來)負值表示相對,如 -50000000 = 5 秒
絕對時間未轉 UTC觸發時間偏移本地時區差必須 LocalFileTimeToFileTime 轉 UTC
忽略 bManualResetTrue=喚醒所有等待線程, False=喚醒單一線程根據同步需求選擇

下一步:APC 回調函數

掌握起始時間控制後,下一階段將深入 SetWaitableTimer 第 4、5 參數:APC 回調函數指標 與 用戶指標參數,實現真正的非阻塞高精度定時任務。

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