C++ std::this_thread::sleep_,直到计时完全停止大约2倍,这是令人费解的

C++ std::this_thread::sleep_,直到计时完全停止大约2倍,这是令人费解的,c++,stl,sleep,C++,Stl,Sleep,好吧,我真的不知道为什么会这样。我目前正在实现一个线程容器,它以分离的方式运行一个无限循环,在每次迭代之间限制一定的速度 标题: class timeloop { public: std::thread thread = { }; bool state = true; void (*function_pointer)() = nullptr; double ratio = 1.0f; std::chrono::nanoseconds elapsed =

好吧,我真的不知道为什么会这样。我目前正在实现一个线程容器,它以分离的方式运行一个无限循环,在每次迭代之间限制一定的速度

标题:

class timeloop
{
public:
    std::thread thread = { };
    bool state = true;

    void (*function_pointer)() = nullptr;

    double ratio = 1.0f;
    std::chrono::nanoseconds elapsed = { };

    timeloop(
        void (*function_pointer)() = nullptr
    );

    void function();
};
定义:

void timeloop::start()
{
    this->thread = std::thread(
        &loop::function,
        this
    );
}

void timeloop::function()
{
    std::chrono::steady_clock::time_point next;

    std::chrono::steady_clock::time_point start;
    std::chrono::steady_clock::time_point end;

    while (
        this->state
        )
    {
        start = std::chrono::high_resolution_clock::now();
        next = start + std::chrono::nanoseconds(
            (long long) (this->ratio * (double) std::chrono::nanoseconds::period::den)
        );

        if (
            this->function_pointer != nullptr
            )
        {
            this->function_pointer();
        }

        /***************************
            this is the culprit
        ***************************/
        std::this_thread::sleep_until(
            next
        );

        end = std::chrono::high_resolution_clock::now();
        this->elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(
            end - start
            );
    }
}
定义代码的行为异常,特别是
std::this_thread::sleep_until
。由于
this->ratio=1.0/128.0
我预计帧率约为128,因此
start
next
的计算值强化了这一点,但它莫名其妙地徘徊在60左右。是的,我试着把
next
除以2,但实际上它降到了40左右

验证正常睡眠时间的额外代码:

auto diff = std::chrono::nanoseconds(
    next - start
).count() / (double) std::chrono::nanoseconds::period::den;
auto equal = diff == this->ratio;
其中equal的计算结果为
true

帧速率计算:

double time = (double) thread_draw->elapsed.count() / (double) std::chrono::nanoseconds::period::den;
double fps = 1.0 / time;
虽然我也使用了外部FPS计数器进行验证(NVIDIA ShadowPlay和RivaTuner/MSI加力),但它们在计算值的+-5范围内

我知道它是std::this_thread::sleep_,直到,因为一旦我把它注释掉,帧速率就会跳到2000左右。嗯

我真的很困惑,尤其是看到我怎么找不到任何其他人有过这个问题的证据。是的,我知道睡眠功能并不完全准确,偶尔也会打嗝,但持续的睡眠时间几乎是预定时间的两倍是荒谬的


我是否误解了编译器选项或其他什么?这绝对不是一个性能问题,我也有理由相信这也不是一个逻辑错误(看看所有计算的结果如何)[除非我在某个地方滥用了chrono]。

没有关于
睡眠的解决方案的保证,直到
,你只能保证线程不会在时间点之前被唤醒。如果您正在实现主游戏循环,请阅读

用睡眠来保证时间是一种可怕的方法。你受操作系统调度程序的支配,例如,我相信Windows的最小睡眠时间大约为10毫秒。(如果实现实际上要求操作系统将线程置于睡眠状态,并且操作系统决定进行上下文切换。)

如果调用
glfwSwapBuffers
或类似命令,则延迟也可能由绘图线程中的VSync引起。这就解释了为什么你的睡眠时间被限制在60FPS,而不是为什么评论
sleep
可以解决这个问题


我猜上面是操作系统的睡眠。我建议你取消睡眠,使用VSync,这是你想要的正确频率。与逻辑线程同步将是一个痛苦的。。。但情况总是这样。

我想你的问题是你没有使用绝对计时,因为你一直在重置绝对时间点
next
相对于当前时间
now()

尝试更改此选项:

while (
    this->state
    )
{
    start = std::chrono::high_resolution_clock::now();
    next = start + std::chrono::nanoseconds(
        (long long) (this->ratio * (double) std::chrono::nanoseconds::period::den)
    );
// ...
对此

// before the loop
next = std::chrono::high_resolution_clock::now();

while (
    this->state
    )
{
    // keep the timing absolute by adding to the absolute time point
    next = next + std::chrono::nanoseconds(
        (long long) (this->ratio * (double) std::chrono::nanoseconds::period::den)
    );

// ...
这样,您只需调用一次
now()
,随后的所有计时都是从该点开始计算的(与相对计时相反)

编辑以添加

此外,我将避免使用
std::chrono::high_resolution_clock
。它通常只是std::chrono::system_clock的别名,当系统时钟试图与internet时间保持同步时,它会受到随机时间更改的影响


使用
std::chrono::staid_clock

这真的很奇怪,因为我在不同的项目中使用了基本相同的代码,而且工作得非常好。也在同一台机器上。绝对没有10毫秒的最小延迟,这是当场,即使在目标更高的帧速率(如200+)。不管怎样,我从那篇文章中收集到的信息基本上是使用while循环,只做操作系统基本上自己做的事情(即使稍微贵一点)。@TARN4T1ON不确定,那么你可以试着做一个简单的程序,睡在主屏幕上,看看你能得到的最小数量是多少。是的,这篇文章更倾向于物理,但对于与时间相关的循环,它是一个很好的建议。它不使用睡眠,正是因为它不可靠。我实际上看了一下我第一次使用这种代码的项目,它在那里也不再工作了。这方面的建设也滞后。真的很时髦。老实说,可能是最近的一些更新,其他一些东西对我来说也不起作用了(比如VisualStudio的Performance Profiler,我其实想用它来了解这一点,但是是的…)。我只使用while循环。谢谢是的优质chrono实现。通过设置与调用
now()
相关的
next
点,每次您失去绝对时间给您的任何优势时。也许这会有帮助:这就是它过去的工作方式,遗憾的是它没有什么不同。而且,这可能会落后太多(如果主函数花费太多太多时间,或者可能是整个时间),在这一点上,它甚至不会限制任何东西。@TARN4T1ON在我看来,如果你想要定期计时,那么你想在这段时间内做的工作必须是可行的。如果主函数花费的时间太长,那么你就无能为力,因为你的基本问题是主函数花费的时间太长。。。但是不管怎样,你现在做的方式将不可避免地漂移,因为它没有固定在一个绝对的时间基础上。@TARN4T1ON我还添加了一个关于
std::chrono::high_resolution_clock
的注释。你应该使用std::chrono::staid_clock你说得对,我在试错阶段更改了它,但没有更改回来。但似乎睡眠比我记忆中的要糟糕得多(尽管我可以发誓几个月前它还不错)。谢谢你的帮助。
// before the loop
next = std::chrono::high_resolution_clock::now();

while (
    this->state
    )
{
    // keep the timing absolute by adding to the absolute time point
    next = next + std::chrono::nanoseconds(
        (long long) (this->ratio * (double) std::chrono::nanoseconds::period::den)
    );

// ...