每 1 秒调用一个函数(精确地)
Calling a function every 1 second (precisely)
我正在用 C++ 编写一个简单的游戏模拟程序,有一个名为 update() 的函数可以更新游戏的当前状态,它必须精确地每 1 秒调用一次。如果我使用这样的循环:
while(//some condition) {
update();
Sleep(1000);
}
那么这个函数就不会每1秒调用一次,而是每(1 + update()的执行时间)调用一次。
我阅读了各种解决方案,例如异步函数、多线程或使用 std::chrono 计算函数的执行时间并从 1000ms 参数中减去它以进入睡眠状态。其中一些对于我的简单案例来说太复杂了,如果我不太了解它们,其他的似乎不安全。
谁能告诉我什么是适合我的要求的解决方案?
提前致谢。
您可以避免将 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:00:00.000,那么您以后的更新应该尽可能接近 2:00:01.000、2:00:02.000 等
为此,您可以将一个线程专用于更新,并在更新后进入睡眠状态直到下一次执行预定更新。 chrono::system_clock::time_point
和 this_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 秒。
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
}
}
确保事情在正确时间发生的最佳方法是在 while 循环中使用 this_thread::sleep_until()
,正如其他一些人所推荐的那样。
我想确保我的循环从使用 chrono time_since_epoch
计算的第二个 next_full_second
时间点的底部开始
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;
}
我正在用 C++ 编写一个简单的游戏模拟程序,有一个名为 update() 的函数可以更新游戏的当前状态,它必须精确地每 1 秒调用一次。如果我使用这样的循环:
while(//some condition) {
update();
Sleep(1000);
}
那么这个函数就不会每1秒调用一次,而是每(1 + update()的执行时间)调用一次。 我阅读了各种解决方案,例如异步函数、多线程或使用 std::chrono 计算函数的执行时间并从 1000ms 参数中减去它以进入睡眠状态。其中一些对于我的简单案例来说太复杂了,如果我不太了解它们,其他的似乎不安全。
谁能告诉我什么是适合我的要求的解决方案? 提前致谢。
您可以避免将 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:00:00.000,那么您以后的更新应该尽可能接近 2:00:01.000、2:00:02.000 等
为此,您可以将一个线程专用于更新,并在更新后进入睡眠状态直到下一次执行预定更新。 chrono::system_clock::time_point
和 this_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 秒。
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
}
}
确保事情在正确时间发生的最佳方法是在 while 循环中使用 this_thread::sleep_until()
,正如其他一些人所推荐的那样。
我想确保我的循环从使用 chrono time_since_epoch
next_full_second
时间点的底部开始
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;
}