Is it possible to define an std::thread and initialize it later?
Asked Answered
B

4

26

My aim is to keep an std::thread object as data member, and initialize it when needed.
I'm not able to do this (as in my code below) because the copy constructor of the std::thread class is deleted. Is there any other way to do it?

class MyClass
{
    public:
        MyClass():DiskJobThread(){};
        ~MyClass();

        void DoDiskJobThread();

    private:
        int CopyThread(const std::wstring & Source, const std::wstring & Target);
        int MoveThread(const std::wstring & Source, const std::wstring & Target);
        std::thread DiskJobThread;
};

MyClass::~MyClass()
{
    DiskJobThread.join();
}

void MyClass::DoDiskJobThread()
{
    std::wstring Source = GetSource();
    std::wstring Target = GetTarget();
    int m_OperationType = GetOperationType();
    if      (m_OperationType == OPERATION_COPY)
    {
        DiskJobThread = std::thread(&MyClass::CopyThread, *this, Source, Target);
    }
    else if (m_OperationType == OPERATION_MOVE)
    {
        DiskJobThread = std::thread(&MyClass::MoveThread, *this, Source, Target);
    }
}
Brahms answered 22/8, 2013 at 9:6 Comment(1)
Pass this instead of dereferencing *this.Bedell
W
13

Your problem is something else - you're passing an instance of MyClass into the thread instead of the pointer to MyClass which the member functions expect. Simply change DoDiskJobThread() like this (do not dereference this):

void MyClass::DoDiskJobThread()
{
    std::wstring Source = GetSource();
    std::wstring Target = GetTarget();
    int m_OperationType = GetOperationType();
    if      (m_OperationType == OPERATION_COPY)
    {
        DiskJobThread = std::thread(&MyClass::CopyThread, this, Source, Target);
    }
    else if (m_OperationType == OPERATION_MOVE)
    {
        DiskJobThread = std::thread(&MyClass::MoveThread, this, Source, Target);
    }
}

You were getting the error because *this resulted in trying to copy MyClass into the thread function, and the copy ctor of your class is deleted (because that of std::thread is deleted). However, the member functions CopyThread and MoveThread require a pointer as the first (hidden) argument anyway.

Live demonstration

Wiltonwiltsey answered 22/8, 2013 at 9:19 Comment(4)
+1, yes, I did not notice the fact that he is passing *this instead of this, you're right ;|Ptolemaist
The '=' operator doesn't allow assigning a new thread object to DiskJobThread variable. I get this error: error C2678: binary '=' : no operator found which takes a left-hand operand of type 'const std::thread' (or there is no acceptable conversion)Brahms
@Brahms There is no const std::thread in the code you posted (and you can see on ideone that my change works). Post your actual code.Wiltonwiltsey
I think you could use the slightly more obscure method of passing std::ref(*this), but that would just very silly.Cadmium
P
18

How about wrapping it in a pointer?

std::unique_ptr<std::thread> thread_ptr;

// Look into std::make_unique if possible
thread_ptr = std::unique_ptr<std::thread>(new std::thread(...));

Edit: And yes, the others have mentioned it and I didn't feel the need to add it here, but in order to avoid more downvote piling, I'll say it: You are passing *this and not this thereby copying an instance of your class. (Problems arise because it's non-copyable. Pass this and you should be good to go.)

Ptolemaist answered 22/8, 2013 at 9:10 Comment(6)
Now thread_ptr is no more copyable and no less movable than the original DiskJobThread, though...Cadmium
@KerrekSB Yes, but if the goal is only to initialize later, I think it shouldn't be a problem ;oPtolemaist
-1: The problem is not with initialising std::thread (as that uses a move assignment op which is fine), so this does not answer the question.Wiltonwiltsey
@Angew Okay, I have updated the answer to at least mention the second part of the problem.Ptolemaist
@MohammadAliBaydoun The problem with your answer is that it's not needed at all - storing std::thread by value is perfectly fine and it can indeed be initialised later, just like the OP is doing it. Storing it in a unique_ptr will just introduce inefficiencies through dynamic allocation/deallocation and indirection.Wiltonwiltsey
@Angew I see that now.Ptolemaist
W
13

Your problem is something else - you're passing an instance of MyClass into the thread instead of the pointer to MyClass which the member functions expect. Simply change DoDiskJobThread() like this (do not dereference this):

void MyClass::DoDiskJobThread()
{
    std::wstring Source = GetSource();
    std::wstring Target = GetTarget();
    int m_OperationType = GetOperationType();
    if      (m_OperationType == OPERATION_COPY)
    {
        DiskJobThread = std::thread(&MyClass::CopyThread, this, Source, Target);
    }
    else if (m_OperationType == OPERATION_MOVE)
    {
        DiskJobThread = std::thread(&MyClass::MoveThread, this, Source, Target);
    }
}

You were getting the error because *this resulted in trying to copy MyClass into the thread function, and the copy ctor of your class is deleted (because that of std::thread is deleted). However, the member functions CopyThread and MoveThread require a pointer as the first (hidden) argument anyway.

Live demonstration

Wiltonwiltsey answered 22/8, 2013 at 9:19 Comment(4)
+1, yes, I did not notice the fact that he is passing *this instead of this, you're right ;|Ptolemaist
The '=' operator doesn't allow assigning a new thread object to DiskJobThread variable. I get this error: error C2678: binary '=' : no operator found which takes a left-hand operand of type 'const std::thread' (or there is no acceptable conversion)Brahms
@Brahms There is no const std::thread in the code you posted (and you can see on ideone that my change works). Post your actual code.Wiltonwiltsey
I think you could use the slightly more obscure method of passing std::ref(*this), but that would just very silly.Cadmium
C
12

You can't initialize the thread object after it's created; by definition, initialization occurs when an object is created. But you can use swap to move a thread object into another:

std::thread thr1; // no thread of execution
std::thread thr2(my_function_object); // creates thread of execution
thr1.swap(thr2);  // thr1 is now running the thread created as thr2
                  // and thr2 has no thread of execution
Cimbalom answered 22/8, 2013 at 13:6 Comment(2)
It doesn't work. throws this error: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory0(611): error C2280: 'std::thread::thread(const std::thread &)' : attempting to reference a deleted function 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\thread(70) : see declaration of 'std::thread::thread' 1> This diagnostic occurred in the compiler generated function 'Abc::Abc(const Abc &)Chicalote
@Chicalote -- the problem is in your class Abc which apparently has a std::thread as a data member.. std::thread is not copyable, so your class has to either disable copy construction or have a copy constructor that somehow makes sense out of trying to copy that thread object.Cimbalom
G
3

My aim is to keep an std::thread object as data member, and initialize it when needed.

Since a default-constructed std::thread object doesn't have an associated thread of execution, you can achieve that by using such an object as the target for a (move) assignment operation. However, note that the following is not initialization, but assignment:

std::thread th; // no thread of execution associated with th object
// ...
th = std::thread(func);

The temporary std::thread object created with std::thread(func) has an associated thread of execution. The ownership of this thread of execution is transferred to th through the move assignment – i.e., th steals the ownership of that thread of execution from the temporary.

Note that if th had an associated thread of execution at the moment of the assignment, std::terminate() would have been called.

Glycerol answered 11/6, 2020 at 22:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.