You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows环境下如何让服务器以120Hz可靠发消息且不占用CPU

Windows下实现120Hz高精度Tick发送方案

要实现约8.33ms间隔的tick发送,同时避免忙循环占用CPU,Windows提供了两种专门的高精度定时机制,比单纯用Sleep()更可靠:

方法一:多媒体定时器(timeSetEvent)

多媒体定时器是Windows针对多媒体场景设计的高精度定时接口,最小精度可达1ms,完全满足120Hz的需求,且定时器触发时会自动唤醒线程,不会占用CPU资源。

实现步骤:

  1. 调用timeBeginPeriod(1)提升系统时钟分辨率到1ms(用完记得恢复);
  2. 注册定时器回调函数,设置触发间隔为8333微秒(接近8.33ms);
  3. 在回调中处理向客户端发送tick的逻辑;
  4. 程序退出时调用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的场景。

实现步骤:

  1. 创建可等待定时器对象;
  2. 设置定时器的触发间隔为8.33ms;
  3. 循环等待定时器触发,每次触发后执行tick发送逻辑;
  4. 程序退出时关闭定时器句柄。

代码示例:

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 01:18:28