C ++ 11: appel d’une fonction C ++ périodiquement

J’ai mis en place une simple classe de temporisateur c ++ censée appeler périodiquement une fonction donnée à partir de divers exemples sur SO, comme suit:

#include  #include  #include  #include  class CallBackTimer { public: CallBackTimer() :_execute(false) {} void start(int interval, std::function 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; }; 

Maintenant, je veux appeler cela depuis une classe C ++ comme suit

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

Cependant, cela appelle avec l’erreur

 terminate called after throwing an instance of 'std::bad_function_call' what(): bad_function_call 

Le problème dans votre code est que votre expression lambda dans votre fonction “start” capture les variables locales par référence, en utilisant la syntaxe [&] . Cela signifie que le lambda capture les variables d’ interval et de func par référence, qui sont toutes les deux des variables locales de la fonction start() et qu’elles disparaissent donc après le retour de cette fonction. Mais, après être revenu de cette fonction, le lambda est toujours vivant dans le fil détaché. C’est à ce moment que vous obtenez l’exception “bad-function-call”, car il tente d’appeler func par référence à un object qui n’existe plus.

Ce que vous devez faire est de capturer les variables locales par valeur, avec la syntaxe [=] sur le lambda, comme suit:

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

Cela fonctionne quand je l’essaie.

Ou, vous pouvez également lister les valeurs que vous souhaitez capturer de manière plus explicite (ce que je recommande généralement pour lambdas):

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

MODIFIER

Comme d’autres l’ont souligné, l’utilisation d’un thread détaché n’est pas une bonne solution car vous pourriez facilement oublier d’arrêter le thread et vous n’avez aucun moyen de vérifier s’il est déjà en cours d’exécution. En outre, vous devriez probablement rendre le drapeau _execute atomique, juste pour vous assurer qu’il n’est pas optimisé et que les lectures / écritures sont thread-safe. Vous pouvez faire ceci à la place:

 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 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 _execute; std::thread _thd; };