简体   繁体   English

c ++ 11 std :: chrono测量经过的时间

[英]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::futurestd::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_typearguments上模板化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_typeargument类型。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM