C ++ 11 thread does not work with virtual member function

I am trying to get a class to start a thread that will call a virtual member function named Tick () in a loop. Then I tried to get the class and override base :: Tick ().

but when executed, the program simply calls the base class Tick instead of overriding it. any solutions?

#include <iostream> #include <atomic> #include <thread> #include <chrono> using namespace std; class Runnable { public: Runnable() : running_(ATOMIC_VAR_INIT(false)) { } ~Runnable() { if (running_) thread_.join(); } void Stop() { if (std::atomic_exchange(&running_, false)) thread_.join(); } void Start() { if (!std::atomic_exchange(&running_, true)) { thread_ = std::thread(&Runnable::Thread, this); } } virtual void Tick() { cout << "parent" << endl; }; std::atomic<bool> running_; private: std::thread thread_; static void Thread(Runnable *self) { while(self->running_) { self->Tick(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } }; class Fn : public Runnable { public: void Tick() { cout << "children" << endl; } }; int main (int argc, char const* argv[]) { Fn fn; fn.Start(); return 0; } 

outputs:

 parent 
+4
source share
2 answers

You cannot let an object run out of scope until you finish using it! return 0; at the end, main causes fn to go out of scope. Therefore, by the time you get to the tick call, there is no guarantee that the object already exists.

(The logic in ~Runnable completely broken. It's too late inside the destructor - the object is already at least partially destroyed.)

+11
source

The approach of using inheritance with a parent serving as control for a thread and child objects that implement functions is a bad idea in general. Common problems with this approach come from construction and demolition:

  • if the thread is started from the constructor in the parent (control), then it can be launched before the constructor completes and the thread can call a virtual function before the complete object is completely constructed.

  • if the thread is stopped in the parent destructor, then by the time the element is attached to the thread, the thread executes a method for an object that no longer exists.

In your particular case, you are in the second case. The program starts execution, and in main second thread starts. At this point, there will be a race between the main thread and the newly launched one, if the new thread is faster (it is unlikely, since the beginning of the thread is an expensive operation), it will call the Tick member method, which will be sent to the final Fn::Tick override.

But if the main thread is faster, it will leave the main area and it will begin to destroy the object, it will complete the destruction of the Fn object, and during the construction of Runnable it will join thread. If the main thread is fast enough, it will go to join before the second thread and wait where the second thread will call Tick on the last final field, which Runnable::Tick . Please note that this is an Undefined Behavior and is not guaranteed, as the second thread accesses the object being destroyed.

In addition, there are other possible orderings, for example, the second thread can send to Fn::Tick before the main thread starts destroying, but may not execute the function before the main thread destroys the Fn subelement, in which case your second the thread will call a member function on a dead object.

You better follow the approach in the C ++ standard: separate the control from the logic, completely build the object that will be launched, and transfer it to the stream during construction. Note that this applies to Java Runnable , which is recommended for extending the Thread class. Note that from a design point of view, this separation makes sense: a thread object controls execution, and runnable controls executable code. A thread is not a ticker, but rather that it controls the execution of a ticker. And in your Runnable code, there might not be something that can be run, but rather something that runs other objects that come from it.

+3
source

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


All Articles