实现高分辨率计时器的最佳方法
Posted
技术标签:
【中文标题】实现高分辨率计时器的最佳方法【英文标题】:Best way to implement a high resolution timer 【发布时间】:2019-09-16 07:59:19 【问题描述】:在 C++11 中是什么,该计时器在循环中连续检查时间,并在经过某个时间点后执行一些代码?例如检查从上午 9 点开始循环的时间,并在上午 11 点准确执行一些代码。我要求时间准确(即上午 9 点后不超过 1 微秒)。
我将在 Linux CentOS 7.3 上实现这个程序,并且在专用 CPU 资源来执行这个任务方面没有问题。
【问题讨论】:
@Aconcagua 是的。在这种情况下,有什么建议吗?std::chrono::high_resolution_clock
:'表示实现提供的最小滴答周期的时钟'。
循环运行对我来说似乎很可疑,但是,如果您需要如此高的精度。至少,您需要确保没有其他进程在同一内核上运行,以避免线程之间的上下文切换。可能,基于中断的解决方案更有可能达到所需的精度。
auto targetTime = std::chrono::high_resolution_clock::now(); /* now get exact time from your atomic clock! With that, calculate exact distance to 11:00 */ targetTime += duration; while(targetTime - std::chrono::high_resolution_clock::now() > 0) /* wait */
(正时差的计算已经简化!)。
1us!哇!好的,循环等待是没有解决办法的。这是实时调度程序的要求。所以你应该考虑在这种情况下使用 RT 内核。但是这样的场景有什么意义。通常,此类事情在板载同步晶体的嵌入式设备上运行。在普通 PC 上保持如此精确的操作系统时钟对我来说听起来是不可能的。你有没有检查过你的 RTC 在你的系统上有多少时间偏差?顺便说一句:你如何与真实的地球时间同步?使用 ntp 协议并没有提供这样的精度!
【参考方案1】:
您可以使用例如,而不是手动实现此功能systemd.timer
。确保指定the desired accuracy,它显然可以精确到 1us。
【讨论】:
是否可以在 c++ 程序中包含 systemd.timer?由于它是更大程序的一部分,我无法将代码分离到可以放在 systemd.timer 上的程序中。 @L.Koh:一个 systemd.timer 启动一个 systemd.service。服务启动了一个程序,但你已经运行了。 @L.Koh 是的:如果您的 C++ 程序使用 dbus,您还可以使用systemd
动态创建和注册计时器。【参考方案2】:
一个在循环中不断检查时间的高分辨率计时器,
首先,您确实不想循环不断地检查时间;这效率极低,根本没有必要。
...经过某个时间点后执行一些代码?
好的,因此您希望在未来的给定时间尽可能准确地运行一些代码。
最简单的方法是简单地启动一个后台线程,计算距离目标时间多长时间(以所需的分辨率),然后让线程在该时间段内休眠。当您的线程唤醒时,它会执行实际任务。这应该足以满足绝大多数需求。
std::chrono
库提供的调用让这一切变得简单:
std::chrono
High resolution clock in std::chrono
这是一个 sn-p 代码,它使用系统时钟执行您想要的操作(这使得设置挂钟时间更容易):
// c++ --std=c++11 ans.cpp -o ans
#include <thread>
#include <iostream>
#include <iomanip>
// do some busy work
int work(int count)
int sum = 0;
for (unsigned i = 0; i < count; i++)
sum += i;
return sum;
std::chrono::system_clock::time_point make_scheduled_time (int yyyy, int mm, int dd, int HH, int MM, int SS)
tm datetime = tm;
datetime.tm_year = yyyy - 1900; // Year since 1900
datetime.tm_mon = mm - 1; // Month since January
datetime.tm_mday = dd; // Day of the month [1-31]
datetime.tm_hour = HH; // Hour of the day [00-23]
datetime.tm_min = MM;
datetime.tm_sec = SS;
time_t ttime_t = mktime(&datetime);
std::chrono::system_clock::time_point scheduled = std::chrono::system_clock::from_time_t(ttime_t);
return scheduled;
void do_work_at_scheduled_time()
using period = std::chrono::system_clock::period;
auto sched_start = make_scheduled_time(2019, 9, 17, // date
00, 14, 00); // time
// Wait until the scheduled time to actually do the work
std::this_thread::sleep_until(sched_start);
// Figoure out how close to scheduled time we actually awoke
auto actual_start = std::chrono::system_clock::now();
auto start_delta = actual_start - sched_start;
float delta_ms = float(start_delta.count())*period::num/period::den * 1e3f;
std::cout << "worker: awoken within " << delta_ms << " ms" << std::endl;
// Now do some actual work!
int sum = work(12345);
int main()
std::thread worker(do_work_at_scheduled_time);
worker.join();
return 0;
在我的笔记本电脑上,典型的延迟约为 2-3 毫秒。如果您使用high_resolution_clock
,您应该能够获得更好的结果。
您还可以使用其他 API,例如 Boost,您可以在其中 use ASIO to implement high res timeout。
我要求时间准确(即上午 9 点后不超过 1 微秒)。
您真的需要精确到微秒吗?考虑到在此分辨率下,您还需要考虑各种其他因素,包括系统负载、延迟、时钟抖动等。您的代码可以在接近那个时间开始执行,但这只是问题的一部分。
【讨论】:
谢谢@gavinb。这很有帮助。我之前也探索过 struct tm,但问题似乎是无法将 tm 定义到至少毫秒?如果我错了,请纠正我。 @L.Kohstruct tm
仅用于定义预定的挂钟时间(以秒为单位),然后将其转换为 std::chrono::time_point
,这要准确得多(至少 ms或更好,具体取决于操作系统/平台)。因此,当计算睡眠时间的时间增量时,它将与std::chrono::system_clock::period
一样精确,在我的机器上是微秒。但正如我所提到的,有各种各样的延迟和影响性能的因素。【参考方案3】:
我的建议是使用timer_create()。这使您可以在给定时间收到信号通知。然后您可以在signal handler 中实施您的操作。
在任何情况下,您都应该知道,准确度当然取决于系统时钟的准确度。
【讨论】:
以上是关于实现高分辨率计时器的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现