[英]c++ 11 std::chrono Measure time Elapsed
I'm working on a Timer class that calls a function once every time interval. 我正在研究一个Timer类,该类每个时间间隔调用一次函数。 I have noticed that the clock is running slightly slow because the function is not taking into account the amount of thine the code took to operate when setting up the wait amount for the clock.
我注意到时钟运行稍慢,因为该函数未考虑设置时钟的等待量时代码所要运行的稀疏量。 I have been having trouble figuring out how to measure the amount of time that has elapsed during the function call and then subtract that from the interval time in order to produce an accurate wait time.
我一直很难弄清楚如何测量函数调用过程中经过的时间,然后从间隔时间中减去该时间以产生准确的等待时间。
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
namespace Engine {
template<class return_type,class...arguments>
class Timer{
typedef std::function<return_type(arguments...)> _function_t;
typedef std::chrono::system_clock::time_point time_point;
typedef std::chrono::duration<size_t,std::micro> _duration;
public:
Timer(size_t interval,bool autoRun,_function_t function,arguments...args){
_function = function;
_interval = interval;
if (autoRun) {
Enable(args...);
}
}
~Timer(){
if (Running()) {
Disable();
}
}
void Enable(arguments...args){
if (!Running()) {
_running=true;
enable(_interval, _function, args...);
}
}
void Disable(){
if (Running()) {
_running=false;
delete _thread;
}
}
volatile bool const& Running()const{
return _running;
}
protected:
void enable(size_t interval,_function_t func,arguments...args){
_thread = new std::thread([&,func,interval,args...](){
while (_running) {
//measure time starting here
func(args...);
//end measurement here
//calculate interval- time elapsed
//use that value in the line below in place of "interval"
std::this_thread::sleep_for(std::chrono::microseconds(interval));
}
});
_thread->detach();
}
protected:
_function_t _function;
volatile bool _running;
size_t _interval;
std::thread* _thread;
};
}
If anyone has a suggestion on how to do this using the std::chrono library please let me know. 如果有人对使用std :: chrono库的操作方法有任何建议,请告诉我。 Please not boost though.
但是请不要提高。 I don't want to have do deal with it at the moment.
我现在不想处理它。
Thanks In Advance. 提前致谢。
EDIT: 编辑:
Here is the updated code: 这是更新的代码:
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <atomic>
namespace Engine {
template<class return_type,class...arguments>
class Timer{
typedef std::function<return_type(arguments...)> _function_t;
typedef std::chrono::system_clock::time_point time_point;
typedef std::chrono::duration<size_t,std::micro> _duration;
public:
Timer(size_t interval,bool autoRun,_function_t function,arguments...args){
_function = function;
_interval = interval;
if (autoRun) {
Enable(args...);
}
}
~Timer(){
if (Running()) {
Disable();
}
}
void Enable(arguments...args){
if (!Running()) {
_running=true;
enable(_interval, _function, args...);
}
}
void Disable(){
if (Running()) {
_running=false;
}
}
std::atomic_bool const& Running()const{
return _running;
}
protected:
void enable(size_t interval,_function_t func,arguments...args){
_thread =std::thread([&,func,interval,args...](){
std::chrono::duration<long long,std::nano> inter(interval);
auto _interval = std::chrono::microseconds(interval);
auto deadline = std::chrono::steady_clock::now();
while (_running) {
func(args...);
std::this_thread::sleep_until(deadline+=_interval);
}
});
_thread.detach();
}
protected:
_function_t _function;
std::atomic_bool _running;
size_t _interval;
std::thread _thread;
};
}
Thanks For the help. 谢谢您的帮助。
while (_running) {
//measure time starting here
func(args...);
//end measurement here
//calculate interval- time elapsed
//use that value in the line below in place of "interval"
std::this_thread::sleep_for(std::chrono::microseconds(interval));
}
Your comments are exactly correct. 您的评论完全正确。 You can find documentation on
std::chrono
here: http://en.cppreference.com/w/cpp/chrono 您可以在以下位置找到有关
std::chrono
文档: http : //en.cppreference.com/w/cpp/chrono
while (_running) {
auto start = std::chrono::high_resolution_clock::now(); //measure time starting here
func(args...);
auto end = std::chrono::high_resolution_clock::now(); //end measurement here
auto elapsed = end - start; //calculate interval- time elapsed
//use that value in the line below in place of "interval"
if (elapsed < interval)
std::this_thread::sleep_for(interval-elapsed);
}
The above assumes you change interval
to be a std::chrono::duration
type. 以上假设您将
interval
更改为std::chrono::duration
类型。 You really should avoid using generic integral types, because you don't get any type safety from them in terms of whether a tick represents a microsecond, a millisecond, or whatever. 您确实应该避免使用泛型整数类型,因为就滴答声表示微秒,毫秒还是任何其他值而言,它们不会为它们提供任何类型安全性。 Users have to check the documentation and that doesn't work very well.
用户必须检查文档,但效果不佳。 Also if you template functions based on the duration then users can pass whatever duration type they like and you can handle any necessary conversion behind the scenes.
同样,如果您根据持续时间来模板化功能,则用户可以传递他们喜欢的任何持续时间类型,并且您可以在后台处理任何必要的转换。
Some other comments. 其他一些评论。
The way you're using variadic templates does not enable perfect forwarding. 您使用可变参数模板的方式无法实现完美的转发。 You may get some extra copies of the parameters besides the one that's needed to ensure the arguments live long enough.
除了可以确保参数有效期足够长的参数外,您还可以获得一些额外的参数副本。
volatile
does not enable atomic accesses. volatile
无法启用原子访问。 Your write to _running
is not sequenced with the reads and therefore causes a data race, leading to undefined behavior. 您对
_running
写入未与读取顺序,因此会导致数据争用,从而导致未定义的行为。 The simplest fix is std::atomic<bool>
, but there are some other possibilities as well. 最简单的解决方法是
std::atomic<bool>
,但是还有其他一些可能性。
The bool autoRun
parameter leads to the so called "boolean trap". bool autoRun
参数导致所谓的“布尔陷阱”。 Instead use an enum that will be more readable. 而是使用将更具可读性的枚举。
You don't need _thread
to be a pointer. 您不需要
_thread
作为指针。 In fact since you immediately detach it and never use it for anything except delete
, you don't need this member at all. 实际上,由于您立即分离了它,并且除了
delete
之外,从未将其用于任何其他用途,因此根本不需要此成员。 But IMO you'd be better off using std::future
and std::async
instead of a detached thread. 但是,IMO最好使用
std::future
和std::async
而不是分离的线程。
There's no point in Enable()
and Disable()
using the public Running()
function since they already have to know about the implementation of Running()
. 使用公共
Running()
函数在Enable()
和Disable()
没有意义,因为他们已经必须了解Running()
的实现。 It's safer to just access _running
directly. 直接访问
_running
更安全。 Another alternative would be to introduce a counterpart to Running()
that is responsible for setting _running
, and then Enable()
and Disable()
would not have to access _running
directly at all. 另一种选择是向
Running()
引入一个负责设置_running
,然后Enable()
和Disable()
完全_running
直接访问_running
。
The detached thread could continue running for a time after the timer object has been destroyed, causing it to access members of the Timer after they are no longer valid. 在计时器对象被销毁后,分离的线程可能会继续运行一段时间,从而导致该线程在不再有效后访问计时器的成员。 If the thread is accessing member variables (such as
_running
) then you must wait for the thread to complete before destruction completes. 如果线程正在访问成员变量(例如
_running
),则必须在销毁完成之前等待线程完成。
Disable()
already checks if the task is running, so the extra check in the destructor is unnecessary. Disable()
已经检查任务是否正在运行,因此不需要在析构函数中进行额外的检查。
The order of arguments in the constructor can be changed so that passing an interval and a function with no autorun or arguments defaults to not auto running and not using ...args
. 可以更改构造函数中参数的顺序,以便传递间隔和不带自动运行或参数的函数默认为不自动运行且不使用
...args
。 Eg auto draw_loop = Timer(microseconds(10), DrawFunc); draw_loop.Enable(foo, bar);
例如
auto draw_loop = Timer(microseconds(10), DrawFunc); draw_loop.Enable(foo, bar);
auto draw_loop = Timer(microseconds(10), DrawFunc); draw_loop.Enable(foo, bar);
It's a good idea to avoid default captures in lambdas because then you may not be sure what's getting captured. 避免在lambda中进行默认捕获是一个好主意,因为这样您可能不确定要捕获的内容。 For example in your code use use reference capture by default, but then capture all the local variables by value.
例如,在您的代码中,默认情况下使用参考捕获,然后按值捕获所有局部变量。 And since
_runnable
is a member variable it does not get captured by reference. 并且由于
_runnable
是成员变量,因此它不会被引用捕获。 Instead the lambda captures this
by value and accesses _runnable
through that. 相反,lambda通过值捕获
this
值,并通过该值访问_runnable
。
You can use the _function
and _interval
member variables in your loop instead of capturing new copies. 您可以在循环中使用
_function
和_interval
成员变量,而不是捕获新副本。
Instead of using std::function
and templating Timer
on return_type
and arguments
you can simply template Timer
on a generic Function
type. 除了使用
std::function
和在return_type
和arguments
上模板化Timer
,您还可以在通用Function
类型上简单地将Timer
模板化。 That way you don't pay the expense of std::function
and don't have unnecessary return_type
and argument
types that you don't use anywhere. 这样,您就不必支付
std::function
的费用,并且不需要在任何地方都不使用的不必要的return_type
和argument
类型。
template<typename Function>
class Timer {
using duration = std::chrono::nanosecond;
public:
enum class AutoRun { no, yes };
template<typename Duration, typename... Arguments>
Timer(Duration interval, Function function, AutoRun run = AutoRun::no, Arguments &&...args)
: _function(function)
, _interval(std::chrono::duration_cast<duration>(interval))
, _running(false)
{
if (AutoRun::yes == run) {
Enable(std::forward<Arguments>(args)...);
}
}
~Timer(){
Disable();
}
template<typename... Arguments>
void Enable(Arguments &&...args){
if (!_running) {
_running=true;
enable(std::forward<Arguments>(args)...);
}
}
void Disable() {
if (_running) {
_running = false;
_thread.get();
}
}
volatile bool const& Running() const {
return _running;
}
protected:
template<typename... Arguments>
void enable(Arguments &&...args) {
_thread = std::async([this] (Arguments &&...args_copy) {
auto time_to_wake = std::chrono::steady_clock::now();
while (_running) {
_function(args_copy...);
time_to_wake += _interval;
std::this_thread::sleep_until(time_to_wake);
}
}, std::forward<Arguments>(args)...);
}
protected:
Function _function;
duration _interval;
std::atomic<bool> _running;
std::future<void> _thread;
};
Use std::this_thread::sleep_until
to keep the intervals between events as uniform as possible: 使用
std::this_thread::sleep_until
可以使事件之间的间隔尽可能均匀:
void enable(size_t interval,_function_t func,arguments...args){
_thread = new std::thread([&,func,interval,args...]{
auto interval = std::chrono::microseconds{this->interval};
auto deadline = std::chrono::steady_clock::now();
while (_running) {
func(args...);
deadline += interval;
std::this_thread::sleep_until(deadline);
}
});
_thread->detach();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.