Sleep() 會釋放 CPU 但阻塞訊息迴路,導致介面凍結、無法處理重繪/使用者輸入。本文整理四種「可處理訊息」的延遲方案,依精度由低至高:
#include // 或 afx.h
COleDateTime start = COleDateTime::GetCurrentTime();
COleDateTimeSpan elapsed;
while (elapsed.GetTotalSeconds() <= 2.0) { // 延遲 2 秒
MSG msg;
if (::GetMessage(&msg, NULL, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
elapsed = COleDateTime::GetCurrentTime() - start;
}
優點:支援分鐘/小時/天 (GetTotalMinutes 等)、完整訊息處理。
缺點:精度約 1 秒,COleDateTime 較重。
DWORD start = GetTickCount();
DWORD elapsed = 0;
while (elapsed <= 2000) { // 2000 ms = 2 秒
MSG msg;
if (::GetMessage(&msg, NULL, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
elapsed = GetTickCount() - start;
}
優點:輕量、~1ms 精度、標準 API。
注意:GetTickCount 49.7 天溢位,長延遲需處理。
LARGE_INTEGER freq, start, now;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
double target_us = 40.0; // 40 微秒
double elapsed_us = 0;
while (elapsed_us < target_us) {
QueryPerformanceCounter(&now);
elapsed_us = (double)(now.QuadPart - start.QuadPart) * 1e6 / freq.QuadPart;
// 如需處理訊息,在此加入 GetMessage/Translate/Dispatch
}
優點:硬體級精度、跨處理器一致。
缺點:忙碌等待佔用 CPU、需自行加入訊息泵。
#define NOP_COUNT 3 // 需依 CPU 指令週期自行校準
__asm {
mov ecx, NOP_COUNT
DELAY:
nop
loop DELAY
}
適用場景:硬體時序控制、極短延遲 (納秒級)。
警告:強烈不建議在應用層使用,受 CPU 主頻、超執行緒、電源管理影響極大,不可移植。
| 方案 | 精度 | 訊息處理 | CPU 佔用 | 適用場景 |
|---|---|---|---|---|
| COleDateTime | ~1 秒 | 完整 | 低 | 長延遲、背景任務輪詢 |
| GetTickCount | ~1 ms | 完整 | 低 | UI 延遲、動畫幀率控制 |
| QueryPerformanceCounter | ~0.1 μs | 需手動加入 | 高 (忙碌等待) | 高精度計時、效能分析 |
| 組語 NOP | ~1 ns | 無 | 100% | 驅動程式、硬體協定 |
SetTimer/WM_TIMER,非阻塞、系統調度GetTickCount + MsgWaitForMultipleObjects (可等待多物件)QueryPerformanceCounter 僅用於測量,不用於延遲class CDelayWait {
DWORD m_start, m_timeout;
bool m_cancelled;
public:
CDelayWait(DWORD timeout_ms) : m_start(GetTickCount()), m_timeout(timeout_ms), m_cancelled(false) {}
void Cancel() { m_cancelled = true; }
bool Wait() {
while (!m_cancelled && (GetTickCount() - m_start) < m_timeout) {
MSG msg;
if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) return false;
::TranslateMessage(&msg);
::DispatchMessage(&msg);
} else {
::Sleep(1); // 釋放 CPU 切片
}
}
return !m_cancelled;
}
};