Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a function every 1 second (precisely)

I am working on a simple game simulation program in C++, there's a function called update() that updates the current state of the game, it has to be called every 1 second precisely. If I use a loop like this:

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

Then the function will not be called every 1 second, instead, every (1 + execution time of update () ). I read about various solutions like async functions, multithreading, or calculating the function's execution time using std::chrono and subtracting it from the 1000ms parameter to sleep. Some of those were too complicated for my simple case, and others seemed unsafe to use if I don't understand them really well.

Can anyone tell me what would be a suitable solution to my requirement? Thanks in advance.

like image 442
Yosry Avatar asked Dec 18 '22 23:12

Yosry


1 Answers

Instead of sleeping for a duration, you need to sleep until a time point. For example, if your first update is at precisely 2:00:00.000, your future updates should come as closely as possible to 2:00:01.000, 2:00:02.000, etc.

To achieve this you can dedicate a thread to updating, and after the update, goes to sleep until the next time to do a scheduled update. chrono::system_clock::time_point and this_thread::sleep_until are your tools to do this.

For example:

#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);
}

Just for demo purposes (not necessary for the logic), I'm using Howard Hinnant's, free, open-source date/time library to print the current time (UTC) to microsecond precision in order to illustrate the stability of this technique. A sample output of this program is:

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
like image 95
Howard Hinnant Avatar answered Dec 24 '22 00:12

Howard Hinnant