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

如何让Windows下的C++线程定时器实现稳定一致的定时?

跨平台C++线程定时器Windows下精度优化方案

问题描述

正在构建跨平台C++线程定时器,现有代码在Raspbian 9系统下定时稳定,周期偏差仅数十微秒,但在Windows 10(MinGW64编译)下表现极差:

  • 设置25ms周期时,实际间隔至少30ms,拖动窗口时误差可达47ms
  • 设置15ms周期时,间隔在15ms和30ms间跳变,无法稳定

现有核心代码如下:

#include <cinttypes>
#include <cstdlib>
#include <iostream>
#include <ostream>
#include <fstream>

#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;

//#define CHOSEN_CLOCK std::chrono::high_resolution_clock
#define CHOSEN_CLOCK std::chrono::steady_clock

class Timer {
    std::atomic<bool> active{true};
  public:
    std::chrono::time_point<CHOSEN_CLOCK> timer_entry_ts;
    template<typename Function>
    void setInterval(Function function, int interval);
    void stop();
};

template<typename Function>
void Timer::setInterval(Function function, int interval) {
  active = true;
  std::thread t([=]() {
    while(active.load()) {
      timer_entry_ts = CHOSEN_CLOCK::now();
      function();
      if(!active.load()) return;
      std::this_thread::sleep_until(timer_entry_ts + std::chrono::milliseconds(interval));
    }
  });
  t.detach();
}

void Timer::stop() {
  active = false;
}

Timer mtimer;
std::chrono::time_point<CHOSEN_CLOCK> last_entry_ts{};

void timer_function(void)
{
  long long int delta_micros = std::chrono::duration_cast<std::chrono::microseconds>(mtimer.timer_entry_ts-last_entry_ts).count();
  printf("%7.3f ms\n", delta_micros/1000.0f);
  last_entry_ts = mtimer.timer_entry_ts;
}

int main()
{
  last_entry_ts = CHOSEN_CLOCK::now();
  mtimer.setInterval(timer_function, 25);
  std::this_thread::sleep_for(std::chrono::milliseconds(2000));
  mtimer.stop();
  printf("Thread stopped\n");

  std::exit(EXIT_SUCCESS);
}

编译与运行情况

  • Raspbian编译命令:

    g++ -g -Wall -Wextra -std=gnu++11 ./main.cpp -o myProject.exe -pthread -lstdc++fs
    

    输出示例(稳定接近25ms):

    ...
     25.080 ms
     25.107 ms
     25.100 ms
     ...
    
  • Windows(MinGW64)编译命令:

    g++ -g -Wall -Wextra -std=gnu++11 ./main.cpp -o myProject.exe -lstdc++fs -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32
    

    输出示例(误差大):

    ...
     30.719 ms
     31.443 ms
     47.346 ms
     ...
    

解决方案

Windows下定时精度差的核心原因是系统时钟分辨率默认较低(15.625ms),且线程优先级不足导致被频繁抢占。以下是针对性修改:

1. 提升系统时钟分辨率

Windows默认系统时钟中断间隔为15.625ms(64Hz),这导致std::this_thread::sleep_until的最小精度无法突破这个值。调用timeBeginPeriod将时钟分辨率提升至1ms,可大幅缩小定时误差。

修改代码:

  • 添加Windows头文件:#include <windows.h>
  • 在main函数开头添加:
    timeBeginPeriod(1); // 提升时钟分辨率到1ms
    
  • 在程序退出前添加:
    timeEndPeriod(1); // 恢复系统默认分辨率
    

2. 提高定时器线程优先级

Windows默认线程优先级为普通级别,容易被其他线程抢占。将定时器线程设置为高优先级,减少被调度延迟的概率。

修改Timer::setInterval中的线程创建逻辑:

template<typename Function>
void Timer::setInterval(Function function, int interval) {
  active = true;
  std::thread t([=]() {
    // 设置线程为最高实时优先级
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
    while(active.load()) {
      timer_entry_ts = CHOSEN_CLOCK::now();
      function();
      if(!active.load()) return;
      std::this_thread::sleep_until(timer_entry_ts + std::chrono::milliseconds(interval));
    }
  });
  t.detach();
}

3. 替换为Windows原生高精度等待(可选)

如果上述修改仍无法满足需求,可直接使用Windows的QueryPerformanceCounter和WaitForSingleObject实现高精度等待,完全绕过标准库的sleep接口:

// 替换原sleep_until代码段
auto next_wake_time = timer_entry_ts + std::chrono::milliseconds(interval);
// 计算需要等待的微秒数
auto wait_us = std::chrono::duration_cast<std::chrono::microseconds>(next_wake_time - CHOSEN_CLOCK::now()).count();
if (wait_us > 0) {
  // 使用高精度事件等待
  HANDLE event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
  WaitForSingleObject(event, wait_us / 1000); // 转换为毫秒,不足1ms则等待0
  CloseHandle(event);
}

效果验证

修改后,Windows下的定时精度可达到与Raspbian相近的水平,25ms周期的偏差会控制在1ms以内,拖动窗口时的误差也会显著降低。

内容的提问来源于stack exchange,提问作者sdbbs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:43:12