【问题标题】:c++ 11 std::chrono Measure time Elapsedc++ 11 std::chrono 测量经过的时间
【发布时间】:2014-07-16 19:03:14
【问题描述】:

我正在开发一个 Timer 类,该类在每个时间间隔调用一次函数。我注意到时钟运行速度稍慢,因为在设置时钟的等待量时,该函数没有考虑代码运行所需的量。我一直无法弄清楚如何测量函数调用期间经过的时间量,然后从间隔时间中减去它以产生准确的等待时间。

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

    };

}

如果有人对如何使用 std::chrono 库提出建议,请告诉我。请不要提升。我暂时不想处理它。

提前致谢。

编辑:

这是更新后的代码:

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

    };

}

感谢您的帮助。

【问题讨论】:

  • (A) volatile 不能以可移植的方式避免 _running 上的数据竞争 - 您应该将其声明为 std::atomic&lt;bool&gt;。 (B) 没有理由通过指针来保存_thread 而不是直接将它放在类中。
  • @Casey 我会考虑到这一点。我将线程作为指针的原因是因为我在将它作为常规变量时遇到了一些麻烦。编辑:没关系我让它工作。谢谢

标签: c++ c++11 time timer chrono


【解决方案1】:
            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));

            }

您的 cmets 完全正确。您可以在此处找到有关 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);

            }

以上假设您将interval 更改为std::chrono::duration 类型。您确实应该避免使用泛型整数类型,因为就刻度是否表示微秒、毫秒或其他方面而言,您无法从它们那里获得任何类型安全性。用户必须检查文档,但效果不是很好。此外,如果您根据持续时间对函数进行模板化,那么用户可以传递他们喜欢的任何持续时间类型,您可以在后台处理任何必要的转换。


其他一些cmets。

您使用可变参数模板的方式无法实现完美转发。除了确保参数存在足够长的时间所需的参数之外,您可能会获得一些额外的参数副本。

volatile 不启用原子访问。您对_running 的写入未与读取排序,因此会导致数据竞争,从而导致未定义的行为。最简单的解决方法是std::atomic&lt;bool&gt;,但也有一些其他的可能性。

bool autoRun 参数导致所谓的“布尔陷阱”。而是使用更具可读性的枚举。

你不需要_thread 是一个指针。事实上,由于您立即将其分离并且从不将其用于除delete 之外的任何东西,因此您根本不需要此成员。但是 IMO 你最好使用 std::futurestd::async 而不是分离线程。

Enable()Disable() 使用公共Running() 函数没有意义,因为他们已经知道Running() 的实现。直接访问_running 更安全。另一种选择是引入负责设置_runningRunning() 的对应项,然后Enable()Disable() 根本不必直接访问_running

在定时器对象被销毁后,分离的线程可以继续运行一段时间,导致它在定时器的成员不再有效后访问它们。如果线程正在访问成员变量(例如_running),那么您必须等待线程完成才能完成销毁。

Disable() 已经检查任务是否正在运行,因此不需要在析构函数中进行额外检查。

可以更改构造函数中参数的顺序,以便传递一个间隔和一个没有自动运行或参数的函数默认为不自动运行并且不使用...args。例如。 auto draw_loop = Timer(microseconds(10), DrawFunc); draw_loop.Enable(foo, bar);

最好避免在 lambda 中使用默认捕获,因为这样您可能无法确定捕获的内容。例如,在您的代码中默认使用引用捕获,然后按值捕获所有局部变量。由于_runnable 是一个成员变量,它不会被引用捕获。相反,lambda 通过值捕获this 并通过它访问_runnable

您可以在循环中使用_function_interval 成员变量,而不是捕获新副本。

您可以简单地在通用 Function 类型上模板化 Timer,而不是使用 std::function 并在 return_typearguments 上模板化 Timer。这样一来,您就无需支付 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;
};

【讨论】:

    【解决方案2】:

    使用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();
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-31
      • 2013-11-12
      • 2012-08-21
      • 1970-01-01
      • 2020-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多