C ++ 11: calling a C ++ function periodically

I have compiled a simple C ++ timer class, which should periodically call this function from various examples on SO as follows:

#include <functional> #include <chrono> #include <future> #include <cstdio> class CallBackTimer { public: CallBackTimer() :_execute(false) {} void start(int interval, std::function<void(void)> func) { _execute = true; std::thread([&]() { while (_execute) { func(); std::this_thread::sleep_for( std::chrono::milliseconds(interval)); } }).detach(); } void stop() { _execute = false; } private: bool _execute; }; 

Now I want to call it from a C ++ class as follows L

 class Processor() { void init() { timer.start(25, std::bind(&Processor::process, this)); } void process() { std::cout << "Called" << std::endl; } }; 

However this causes an error

 terminate called after throwing an instance of 'std::bad_function_call' what(): bad_function_call 
+6
source share
1 answer

The problem with your code is that your lambda expression inside your "start" function captures local variables by reference using the [&] syntax. This means that the lambda captures the interval and func variables by reference, which are both local variables for the start() function, and disappear after returning from this function. But, having returned from this function, the lambda is still alive inside a separate thread. This is when you get a bad-function-call exception because it tries to call func reference to an object that no longer exists.

What you need to do is to capture local variables by value using the syntax [=] on lambda, like this:

 void start(int interval, std::function<void(void)> func) { _execute = true; std::thread([=]() { while (_execute) { func(); std::this_thread::sleep_for( std::chrono::milliseconds(interval)); } }).detach(); } 

It works when I try.

Or you can also specify the values ​​you want to get more explicitly (which I usually recommend for lambdas):

 void start(int interval, std::function<void(void)> func) { _execute = true; std::thread([this, interval, func]() { while (_execute) { func(); std::this_thread::sleep_for( std::chrono::milliseconds(interval)); } }).detach(); } 

EDIT

As others have pointed out, using a dedicated thread is not a great solution, because you can easily forget to stop a thread, and you have no way to check if it is running. In addition, you should probably make the _execute flag _execute atom, just to make sure that it is not optimized and that read / write is thread safe. You could do this instead:

 class CallBackTimer { public: CallBackTimer() :_execute(false) {} ~CallBackTimer() { if( _execute.load(std::memory_order_acquire) ) { stop(); }; } void stop() { _execute.store(false, std::memory_order_release); if( _thd.joinable() ) _thd.join(); } void start(int interval, std::function<void(void)> func) { if( _execute.load(std::memory_order_acquire) ) { stop(); }; _execute.store(true, std::memory_order_release); _thd = std::thread([this, interval, func]() { while (_execute.load(std::memory_order_acquire)) { func(); std::this_thread::sleep_for( std::chrono::milliseconds(interval)); } }); } bool is_running() const noexcept { return ( _execute.load(std::memory_order_acquire) && _thd.joinable() ); } private: std::atomic<bool> _execute; std::thread _thd; }; 
+16
source

Source: https://habr.com/ru/post/987826/


All Articles