Windows环境下如何让服务器以120Hz可靠发消息且不占用CPU
Windows下实现120Hz高精度Tick发送方案
要实现约8.33ms间隔的tick发送,同时避免忙循环占用CPU,Windows提供了两种专门的高精度定时机制,比单纯用Sleep()更可靠:
方法一:多媒体定时器(timeSetEvent)
多媒体定时器是Windows针对多媒体场景设计的高精度定时接口,最小精度可达1ms,完全满足120Hz的需求,且定时器触发时会自动唤醒线程,不会占用CPU资源。
实现步骤:
- 调用
timeBeginPeriod(1)提升系统时钟分辨率到1ms(用完记得恢复); - 注册定时器回调函数,设置触发间隔为8333微秒(接近8.33ms);
- 在回调中处理向客户端发送tick的逻辑;
- 程序退出时调用
timeKillEvent销毁定时器,并用timeEndPeriod(1)恢复系统时钟分辨率。
代码示例:
#include <windows.h> #include <mmsystem.h> #pragma comment(lib, "winmm.lib") // 向客户端发送Tick的业务逻辑 void SendTickToClients() { // 此处实现实际的tick发送代码 } // 定时器回调函数 void CALLBACK TimerCallback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) { SendTickToClients(); } int main() { // 提升系统时钟分辨率至1ms timeBeginPeriod(1); // 创建周期性定时器:间隔8333微秒(≈8.33ms) UINT timerId = timeSetEvent(8, 1, TimerCallback, 0, TIME_PERIODIC); if (timerId == 0) { // 处理定时器创建失败逻辑 return 1; } // 保持程序运行(实际业务中可替换为服务启停逻辑) getchar(); // 清理资源 timeKillEvent(timerId); timeEndPeriod(1); return 0; }
注意事项:
- 回调函数必须简洁,禁止执行耗时操作(如阻塞IO),否则会破坏定时精度;
- 必须配对调用
timeBeginPeriod和timeEndPeriod,否则会导致系统整体功耗上升。
方法二:可等待定时器(Waitable Timer)
可等待定时器是内核对象,通过WaitForSingleObject等待定时器触发时,线程会进入休眠状态,完全不占用CPU,精度同样可达1ms级别,适合需要在主线程处理tick的场景。
实现步骤:
- 创建可等待定时器对象;
- 设置定时器的触发间隔为8.33ms;
- 循环等待定时器触发,每次触发后执行tick发送逻辑;
- 程序退出时关闭定时器句柄。
代码示例:
#include <windows.h> // 向客户端发送Tick的业务逻辑 void SendTickToClients() { // 此处实现实际的tick发送代码 } int main() { // 创建可等待定时器对象 HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL); if (hTimer == NULL) { // 处理定时器创建失败逻辑 return 1; } // 设置定时参数:8.33ms = 8333000纳秒(负数值表示相对时间) LARGE_INTEGER liDueTime; liDueTime.QuadPart = -8333000; // 设置定时器周期性触发 if (!SetWaitableTimer(hTimer, &liDueTime, 8, NULL, NULL, FALSE)) { CloseHandle(hTimer); return 1; } // 循环等待定时器触发 while (true) { DWORD waitResult = WaitForSingleObject(hTimer, INFINITE); if (waitResult == WAIT_OBJECT_0) { SendTickToClients(); } else { // 处理等待异常逻辑 break; } } // 清理资源 CloseHandle(hTimer); return 0; }
注意事项:
- 可等待定时器的时间参数以100纳秒为单位,计算时需注意转换;
- 若需调整定时间隔,可重新调用
SetWaitableTimer修改参数。
备选方案:Sleep配合时钟分辨率提升
如果不想使用专用定时器,也可以先提升系统时钟分辨率,再结合Sleep()和时间计算调整间隔,但精度略逊于前两种方法:
#include <windows.h> #include <mmsystem.h> #pragma comment(lib, "winmm.lib") void SendTickToClients() { // 此处实现实际的tick发送代码 } int main() { timeBeginPeriod(1); DWORD lastTickTime = GetTickCount(); while (true) { SendTickToClients(); // 计算并调整下一次Sleep的时长 DWORD currentTime = GetTickCount(); DWORD elapsed = currentTime - lastTickTime; DWORD sleepTime = 8 - elapsed; if (sleepTime > 0) { Sleep(sleepTime); } lastTickTime = currentTime; } timeEndPeriod(1); return 0; }
这种方法需要实时计算已流逝时间,避免Sleep时长偏差,但仍可能受系统调度影响出现小误差。
内容的提问来源于stack exchange,提问作者Wail3Y
相关产品推荐
相关产品推荐

