#include #include #include #pragma comment( lib, "Winmm" )static int counter = 0;static int64_t ticks_per_second;void __stdcall on_timer(HWND h, UINT ui, UINT_PTR up, DWORD dw){    std::cout << "time out, counter=" << counter << std::endl;    counter = 0;}void get_message_trd_func(){    SetTimer(NULL, 0, 1000, on_timer);    MSG msg;    while (GetMessageA(&msg, NULL, 0, 0))    {        TranslateMessage(&msg);        DispatchMessageA(&msg);    }}int main(){    std::cout << "go!" << std::endl;    timeBeginPeriod(1);    QueryPerformanceFrequency((LARGE_INTEGER*)&ticks_per_second);    const double expected = 1.0 / 60.0;    const int64_t expected_ticks = (int64_t)(expected * ticks_per_second);    std::thread thr(get_message_trd_func);    for (;;)    {        int64_t before_ticks = 0;        QueryPerformanceCounter((LARGE_INTEGER*)&before_ticks);        // do something...        for (int i = 0; i = 1.0)            Sleep((DWORD)ms_need_sleep);        else            continue;    }}

这里主要用到的几个 win32api 为

  • MMRESULT timeBeginPeriod
    使用该 api 需要链接 Winmm, 所以我们在文件顶部加入#pragma comment( lib, "Winmm" ).
    它的作用是请求提高一些计时器的精度比如这里的Sleep, 默认 windows 似乎只会给我们提供 10ms 左右很粗糙的精度, 所以这里我们直接请求尽可能的高的精度, 即 1ms.

  • QueryPerformanceFrequency
    该 api 用于获取”性能计时器”的精度, 单位是 ticks每秒. 在我的机子上它的值是 10000000, 可以看到精度还是很令人满意的. 在这里我们将其与expected(也就是期望每次调用的间隔,单位s)进行相乘, 得到一个以ticks为单位的间隔.

  • QueryPerformanceCounter
    该 api 会检测”性能计时器”的值, 单位为 ticks, 在 msdn 中其精度的描述为 <1us.

那么结合上述几个 api 以及几个简单的数学运算, 这样就可以相对稳定的定时调用函数了(在这里是 1s 60 次):

time out, counter=59time out, counter=59time out, counter=60time out, counter=59time out, counter=58time out, counter=60

当你注释掉timeBeginPeriod的调用后你会发现结果不是很乐观(即使我们期望 1s 调用 60 次):

time out, counter=33time out, counter=31time out, counter=32time out, counter=31

最后, 这个可能常见于游戏的帧率控制, 实际上我就是从这里知道的这些东西(x