Is there a safe way to have a std::thread as a member of a class?
Asked Answered
R

4

32

I would like to use a class that manages a thread (or several threads). Using composition, this would look like:

class MyClass{
private:
    std::thread mythread;
    void _ThreadMain();
public:
    MyClass();
    // other fields
}

Because the default constructor for an std::thread is pointless, I would need to call it explicitly in MyClass constructor:

MyClass::MyClass() : mythread(&MyClass::_ThreadMain,this) {}

However, in this case the _ThreadMain method will be likely executed before MyClass is constructed, leading to any kind of weird behaviour. This is clearly unsafe. How can I fix this?

An obvious solution would be to use a pointer to std::thread instead, and add another member function:

void MyClass::Start(){
    // This time mythread is of type  std::thread*
    mythread = new std::thread(&MyClass::_ThreadMain,this); // One could use std::unique_pointer instead.
}

which would fire up that thread. In this case it would be called after the class is constructed, which will be indeed safe.

However, I am wondering if there is any reasonable solution that would allow me not to use pointers for this. It feels like it should be possible somehow (hey, there must be a way to launch a thread when a class is constructed!), but I cannot come up with anything that would not cause troubles.

I have considered using a conditional variable so that the _ThreadMain waits till the constructor has done its work, but I cannot use one before the class is constructed, right? (This would also be unhelpful if MyClass was a derived class)

Rook answered 11/5, 2014 at 15:9 Comment(5)
Omit the new at mythread = new std::thread(&MyClass::_ThreadMain,this);, std::thread is movable! Consider std::bind to bind your class member function.Mustang
You can let the thread wait (at the very start) until it's told to proceed.Pettaway
The problem here is that if the _ThreadMain method is virtual, the thread will call the wrong function. I'd be very interested in seeing a solution to this where the constructor does the right thing.Jp
You're using a reserved identifier.Passible
possible duplicate of C++11: std::thread inside a class executing a function member with thread initialisation in the constructorSenseless
K
11

There is no better way, in general, than having a separate Start function.

Suppose MyClass is a base class for some future (unknown) class Derived. If the thread is started while (or before) the MyClass constructor runs, it always risks calling the "wrong" implementation of some virtual function overridden by Derived.

The only way to avoid this is to have the thread wait until after Derived is fully constructed, and the only way to do that is to call some other function after the Derived constructor completes to tell the thread to "go"... Which means you must have some kind of separately-invoked Go function.

You might as well just have a separately-invoked Start function instead and forego the complexity of waiting.

[Update]

Note that, for complex classes, "Two-Phase Construction" is an idiom recommended by some. Starting the thread would fit seamlessly into the "initialization" phase.

Kinship answered 11/5, 2014 at 15:56 Comment(0)
C
16

You can use a thread in combination with move semantics:

class MyClass final
{
private:
    std::thread mythread;
    void _ThreadMain();
public:
    MyClass()
        : mythread{} // default constructor
    {
        // move assignment
        mythread = std::thread{&MyClass::_ThreadMain, this};
    }
};

The move assignment operator is documented on the following page. In particular, it is noexcept and no new thread is created.

Cheboksary answered 11/5, 2014 at 15:14 Comment(7)
When you move a thread object does it start a new thread of execution, or does it actually move it?Canticle
This solution can run into trouble, if there are is a vtable pointer! The vtable pointer will be initialzed after the block of the constructor!Senseless
@Klaus: You are right. However, you can run into all kind of trouble when using threads. I have made the class final now.Cheboksary
Final will not help. If your class derives from a class with a virtual function every call to this virtual function will go to the base class version of the function until the vtable pointer was initialized. The only way to get this work is to forbid that the class itself has virtual functions or derives from a class with virtual functions. Maybe some SFINAE or static_assert tricks can test for that.Senseless
@Klaus: In the body of constructor for class T, the dynamic type is T. So final does help very much with safety, but at the cost of preventing customization by overriding member functions. I think this solution is therefore not very general.Pettaway
This is the actual answer, ie. starting the thread later on without using a pointer to std::thread. OP doesn't want to start the thread in the constructor anyways.Diannediannne
@πάνταῥεῖ could you elaborate on the templates you're referring to?Viipuri
K
11

There is no better way, in general, than having a separate Start function.

Suppose MyClass is a base class for some future (unknown) class Derived. If the thread is started while (or before) the MyClass constructor runs, it always risks calling the "wrong" implementation of some virtual function overridden by Derived.

The only way to avoid this is to have the thread wait until after Derived is fully constructed, and the only way to do that is to call some other function after the Derived constructor completes to tell the thread to "go"... Which means you must have some kind of separately-invoked Go function.

You might as well just have a separately-invoked Start function instead and forego the complexity of waiting.

[Update]

Note that, for complex classes, "Two-Phase Construction" is an idiom recommended by some. Starting the thread would fit seamlessly into the "initialization" phase.

Kinship answered 11/5, 2014 at 15:56 Comment(0)
L
1

Consider separating the task from the thread management and launching.

One class creates a runner and any synchronization primitives snd the like, The other handles launching it. This allows construction of the runnable to fail before threading starts.

It also means the runnable is fully constructed prior to it being run.

Now a first pass would have the runner be a std::thread, but some stuff helping with abort and cleanup and continuations can be useful.

The run object could be a simple callable, or could add extra supportmfor the runnable to interact with it.

Larrup answered 11/5, 2014 at 18:50 Comment(0)
R
0

There is no need to abuse pointers. Two things to note:

  • std::thread has a move assignment operator that we can use to start it after initialization
  • std::thread needs to be detached or joined properly (the accepted answer fails at that)
class MyClass {
private:
    std::thread mythread;
    void ThreadMain(); // note: names like _ThreadMain are reserved; remove the _
public:
    MyClass() {
        // can't init mythread in member initializer list because it might result
        // in _ThreadMain() using this object before it's fully initialized
        mythread = std::thread(&MyClass::ThreadMain, this);
    }

    ~MyClass() {
        mythread.join(); // remember to join here, or to detach in the default constructor
    }

    // other fields ...
}

Note that ThreadMain() could be executed before MyClass has been fully initialized. However, this doesn't really matter, because all of its subobjects have been initialized at that point. It would only be a problem in the event that MyClass is a polymorphic class, in which case you should create a separate Start() member function.

I have considered using a conditional variable so that the _ThreadMain waits till the constructor has done its work, but I cannot use one before the class is constructed, right?

You can use one, if you want to, but it's not really necessary. If mythread = ... is the last thing executed by the constructor of MyClass, then all the other subobjects (including the condition variable) are ready and safe to use. However, since that's guaranteed, you probably don't need a condition variable at all.

However, I am wondering if there is any reasonable solution that would allow me not to use pointers for this.

You can do the same thing without member function pointers, if it's just a matter of style:

mythread = std::thread([this]() { ThreadMain(); });

Note: If you are using C++20, you can avoid writing a destructor and simply use std::jthread instead of std::thread.

Redundancy answered 5/9, 2023 at 15:1 Comment(4)
This doesn't address the race condition between the constructor and the started thread though.Benedetto
@AlexeyVeleshko what race condition are we talking about here? The answer does resolve OP's problem.Redundancy
The instance is only fully constructed when the constructor is complete. For example, the vtable is only finalized when the construction is over.Benedetto
@AlexeyVeleshko yes, but as my answer has pointed out, it's only a problem if we are dealing with a polymorphic class, and OP's question does not mention anything about polymorphic classes or virtual member functions.Redundancy

© 2022 - 2024 — McMap. All rights reserved.