std::this_thread::sleep_for 睡得太久

std::this_thread::sleep_for sleeps for too long

谁能说出以下示例的问题所在?

它每秒产生 65 帧而不是 300 帧。

#define WIN32_LEAN_AND_MEAN

#include <Windows.h>

#include <Thread>
#include <Chrono>
#include <String>

int main(int argc, const char* argv[]) {

    using namespace std::chrono_literals;

    constexpr unsigned short FPS_Limit = 300;

    std::chrono::duration<double, std::ratio<1, FPS_Limit>> FrameDelay = std::chrono::duration<double, std::ratio<1, FPS_Limit>>(1.0f);

    unsigned int FPS = 0;

    std::chrono::steady_clock SecondTimer;
    std::chrono::steady_clock ProcessTimer;

    std::chrono::steady_clock::time_point TpS = SecondTimer.now();
    std::chrono::steady_clock::time_point TpP = ProcessTimer.now();

    while (true) {

        // ...

        // Count FPS

        FPS++;

        if ((TpS + (SecondTimer.now() - TpS)) > (TpS + 1s)) {

            OutputDebugString(std::to_string(FPS).c_str()); OutputDebugString("\n");

            FPS = 0;

            TpS = SecondTimer.now();
        }

        // Sleep

        std::this_thread::sleep_for(FrameDelay - (ProcessTimer.now() - TpP));    // FrameDelay minus time needed to execute other things

        TpP = ProcessTimer.now();
    }

    return 0;
}

我猜这与 std::chrono::duration<double, std::ratio<1, FPS_Limit>> 有关,但当它乘以 FPS_Limit 时,每秒产生正确的 1 帧。

请注意,每秒 300 帧的限制只是一个示例。 它可以替换为任何其他数字,程序仍然会休眠太久。

简而言之,问题是你根本就用了std::this_thread::sleep_for。或者,就此而言,任何类型的 "sleep"。睡觉限制帧率是完全错误的。

睡眠功能的目的是,好吧...老实说我不知道​​。它几乎没有什么好的用途,实际上在每种情况下,不同的机制会更好。

std::this_thread::sleep_for 所做的(提供或进行几行完整性测试和错误检查)是,它调用 Win32 Sleep 函数(或者,在不同的 OS 上,一个不同但相似的函数,例如 nanosleep).

现在,Sleep 做什么?它会在操作系统的小红皮书某处注明您的线程需要在未来的某个时间再次准备就绪,然后呈现您的线程 not-ready。未准备好仅意味着您的主题不在计划获得 CPU 时间的候选人列表中。

有时,硬件计时器最终会触发中断。这可以是一个周期性定时器(pre Windows 8),默认分辨率差得令人尴尬,或者是可编程的单次中断,等等。您甚至可以调整计时器的分辨率,但这样做是 global 的事情,会大大增加上下文切换的次数。另外,它没有解决 实际 问题。当 OS 处理中断时,它会在其簿中查找哪些线程需要准备就绪,然后它就会这样做。

但是,与运行您的线程不同。它只是再次成为 运行 的候选者(也许,某个时候)。

因此,存在计时器粒度、测量不准确以及日程安排等问题……这些都非常非常不适合短的周期性间隔。此外,已知不同的 Windows 版本会根据调度程序的粒度进行不同的舍入。

解决办法:不要睡觉。启用垂直同步,或让用户自行启用。