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; };
source share