我正在研究一个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;
};
}
谢谢你的帮助。
答案 0 :(得分:3)
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));
}
您的评论完全正确。您可以在此处找到关于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
类型。你真的应该避免使用泛型整数类型,因为你不能从它们获得任何类型的安全性,无论刻度是代表微秒,毫秒,还是其他什么。用户必须检查文档并且不能很好地工作。此外,如果您根据持续时间模拟功能,那么用户可以传递他们喜欢的任何持续时间类型,您可以在幕后处理任何必要的转换。
其他一些评论。
您使用可变参数模板的方式无法实现完美转发。除了确保参数足够长时间所需的参数之外,您可以获得一些额外的参数副本。
volatile
不启用原子访问。您对_running
的写入未按读取顺序排列,因此会导致数据争用,从而导致未定义的行为。最简单的修复方法是std::atomic<bool>
,但也有其他一些可能性。
bool autoRun
参数导致所谓的&#34;布尔陷阱&#34;。而是使用更易读的枚举。
您不需要_thread
作为指针。事实上,既然你立即将其分离并且从不将它用于delete
以外的任何事情,那么你根本就不需要这个成员。但IMO最好不要使用std::future
和std::async
代替分离的帖子。
Enable()
和Disable()
使用公共Running()
函数没有任何意义,因为他们必须知道Running()
的实现。直接访问_running
会更安全。另一种方法是引入负责设置Running()
的{{1}}对应方,然后_running
和Enable()
不必直接访问Disable()
所有
分离的线程可能会在计时器对象被销毁后继续运行一段时间,导致它在不再有效后访问Timer的成员。如果线程正在访问成员变量(例如_running
),则必须等待线程在销毁完成之前完成。
_running
已经检查任务是否正在运行,因此不必在析构函数中进行额外检查。
可以更改构造函数中参数的顺序,以便传递间隔和没有自动运行或参数的函数默认为不自动运行而不使用Disable()
。例如。 ...args
避免使用lambda中的默认捕获是一个好主意,因为那时你可能不确定捕获的内容是什么。例如,在代码中,默认情况下使用引用捕获,但随后按值捕获所有局部变量。由于auto draw_loop = Timer(microseconds(10), DrawFunc); draw_loop.Enable(foo, bar);
是一个成员变量,因此它不会被引用捕获。相反,lambda按值捕获_runnable
并通过它访问this
。
您可以在循环中使用_runnable
和_function
成员变量,而不是捕获新副本。
您可以在通用_interval
类型上简单地模板std::function
,而不是在Timer
和return_type
上使用arguments
和模板Timer
。这样您就不会支付Function
的费用,也不会有任何您不会在任何地方使用的std::function
和return_type
类型。
argument
答案 1 :(得分:1)
使用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();
}