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.