std::this_thread::sleep_for 睡眠时间太长

Rig*_*gid 4 c++ sleep frame-rate

谁能说出以下示例的问题是什么?

它每秒生成 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

Dam*_*mon 6

总而言之,问题在于你std::this_thread::sleep_for根本用不上。或者,任何类型的“睡眠”。通过睡眠来限制帧速率是完全错误的。

睡眠功能的目的是,嗯……老实说我不知道​​。它几乎没有什么好的用途,而且在几乎每种情况下,不同的机制都更好。

std::this_thread::sleep_for(进行几行健全性测试和错误检查)是,它调用 Win32函数Sleep(或者,在不同的操作系统上,调用不同的、类似的函数,例如nanosleep)。

现在,Sleep做什么呢?它在操作系统的小红书中的某个地方做了一个注释,您的线程需要在将来的某个时间再次准备好,然后将您的线程渲染为not-ready。未准备好意味着您的线程不在计划获取 CPU 时间的候选者列表中。

有时,最终硬件定时器会触发中断。这可以是一个默认分辨率糟糕得令人尴尬的周期性计时器(Windows 8 之前的版本),也可以是可编程的一次性中断,等等。您甚至可以调整计时器的分辨率,但这样做是全局性的,会大大增加上下文切换的数量。另外,它并不能解决实际问题。当操作系统处理中断时,它会在其书中查找哪些线程需要准备好,然后它就会这样做。

然而,这与运行线程不同它只是再次运行的候选者(也许,某个时候)。

因此,存在计时器粒度、测量不准确以及调度……这非常非常不适合短的周期性间隔。此外,已知不同的 Windows 版本对调度程序的粒度进行不同的舍入。

解决办法:不要睡觉。启用垂直同步,或留给用户启用。

  • `std::this_thread::sleep_until` 的良好实现确实应该使用计时器,但不幸的是,并不能保证一定如此。如果您想知道为什么计时器会更好,有几个原因。也许最引人注目的一点是,即使在 Windows 等非 RT 操作系统(甚至是 15 年前的版本!)上,计时器的工作也更加准确,因为在计时器触发时,您的线程的优先级会提高两个切片。这并不是硬性保证它会尽快运行,而是“一样好”。 (3认同)