每1秒调用一次功能(精确地)

时间:2018-05-02 14:01:30

标签: c++ multithreading asynchronous delay sleep

我正在使用C ++编写一个简单的游戏模拟程序,其中有一个名为update()的函数可以更新游戏的当前状态,它必须每1秒精确调用一次。如果我使用这样的循环:

while(//some condition) {
     update();
     Sleep(1000);
}

然后,每1秒钟不会调用该函数,而是每隔1(执行更新时间(1))调用该函数。 我阅读了各种解决方案,如异步函数,多线程,或使用std :: chrono计算函数的执行时间,并将其从1000ms参数中减去sleep。其中一些对于我的简单案例来说太复杂了,如果我不太了解它们,其他一些似乎不安全。

有谁能告诉我什么是我的要求的合适解决方案? 提前谢谢。

4 个答案:

答案 0 :(得分:6)

而不是在一段时间内睡觉,你需要睡眠直到一个时间点。例如,如果您的第一次更新时间恰好是2:00:00.000,则您的未来更新应尽可能接近2:00:01.000,2:00:02.000等。

要实现此目的,您可以将一个线程专用于更新,并在更新后进入睡眠状态,直到下次进行预定更新。 chrono::system_clock::time_pointthis_thread::sleep_until是您执行此操作的工具。

例如:

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

class UpdateManager
{
public:
    explicit UpdateManager() = default;

private:
    static std::atomic<int> now_;
    static std::atomic<bool> stop_;

    struct update_thread
        : private std::thread
    {
        ~update_thread();
        update_thread(update_thread&&) = default;

        using std::thread::thread;
    };

public:
    static update_thread start();
};

void update();

// source

std::atomic<int>  UpdateManager::now_{0};
std::atomic<bool> UpdateManager::stop_{false};

UpdateManager::update_thread::~update_thread()
{
    if (joinable())
    {
        stop_ = true;
        join();
    }
}

UpdateManager::update_thread
UpdateManager::start()
{
    return update_thread{[]
                         {
                             using namespace std;
                             using namespace std::chrono;
                             auto next = system_clock::now() + 1s;
                             while (!stop_)
                             {
                                 update();
                                 this_thread::sleep_until(next);
                                 next += 1s;
                             }
                         }};
}

#include "date/date.h"

void
update()
{
    using namespace date;
    using namespace std;
    using namespace std::chrono;
    cerr << system_clock::now() << '\n';
}

// demo

int
main()
{
    auto t = UpdateManager::start();
    using namespace std;
    this_thread::sleep_for(10s);
}

仅用于演示目的(逻辑不需要),我使用Howard Hinnant's, free, open-source date/time library将当前时间(UTC)打印到微秒精度,以说明此技术的稳定性。该程序的示例输出是:

2018-05-02 15:14:25.634809
2018-05-02 15:14:26.637934
2018-05-02 15:14:27.636629
2018-05-02 15:14:28.637947
2018-05-02 15:14:29.638413
2018-05-02 15:14:30.639437
2018-05-02 15:14:31.637217
2018-05-02 15:14:32.637895
2018-05-02 15:14:33.637749
2018-05-02 15:14:34.639084

答案 1 :(得分:1)

您可以避免使用chrono来代替这个更简单的选项。但是,如果你想要μs-ns准确度,我强烈建议使用chrono。

#include <ctime>

while(//some condition) {

     std::clock_t start = std::clock();
     update();
     std::clock_t end   = std::clock();
     Sleep(1000 - (end - start)/CLOCKS_PER_SEC * 1000);
}

答案 2 :(得分:1)

确保某事在正确的时间发生的最佳方法是在 while 循环中使用 this_thread::sleep_until(),正如其他一些人所推荐的那样。

我喜欢确保我的循环从使用 chrono next_full_second 计算的 time_since_epoch 时间点的第二个底部开始

interval 变量可以设置为秒或毫秒。使用 open source date library by HowardHinnant 打印出合适的时间值。

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

#include "date/date.h"

using namespace date;
using namespace std;
using namespace std::chrono;

int main()
{

    system_clock::time_point now = system_clock::now();
    auto s = duration_cast<seconds>(now.time_since_epoch());
    system_clock::time_point next_full_second = system_clock::time_point(++s);

    auto interval = seconds(1); // or milliseconds(500) or whatever
    auto wait_until = next_full_second;

    while (1)
    {
        this_thread::sleep_until(wait_until);
        cout << system_clock::now() << endl;

        // do something

        wait_until += interval;
    }

    return 0;
}

答案 3 :(得分:0)

请参阅此伪代码。如果每个进程(可能需要超过1秒)是在异步的单独线程中完成的,则可以根据需要在下一个循环之前准确地休眠1秒。

void MainGameLoop(bool& running)
{
    while (running)
    {
        AnimateSpritesAsync();
        AnimateBulletsAsync();
        CheckCollisionAsync();
        CheckWinConditionsAsync();

        WaitPreciselyOneSecond();
    }
}

例如,您的update函数将如下所示:

void update(); //your update 

void UpdateAsync()  // the async update
{
   std::thread([]() { update(); }).detach();
}

现在你可以:

void MainGameLoop(bool& running)
    {
        while (running)
        {
            UpdateAsync();    
            Sleep(1000); // i won't recommend Sleep tho
        }
    }