是否可以定义一个 std::thread 并稍后对其进行初始化?

Posted

技术标签:

【中文标题】是否可以定义一个 std::thread 并稍后对其进行初始化?【英文标题】:Is it possible to define an std::thread and initialize it later? 【发布时间】:2013-08-24 22:06:38 【问题描述】:

我的目标是保留std::thread 对象作为数据成员,并在需要时对其进行初始化。 我无法执行此操作(如下面的代码所示),因为 std::thread 类的复制构造函数已被删除。还有其他方法吗?

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);
    

【问题讨论】:

传递 this 而不是取消引用 *this 【参考方案1】:

用指针包裹它怎么样?

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

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

编辑: 是的,其他人已经提到它,我觉得没有必要在这里添加它,但为了避免更多的反对意见堆积,我会说:你是通过 *this 而不是 this 从而复制您的类的实例。 (出现问题是因为它是不可复制的。通过this,你应该很高兴。)

【讨论】:

现在thread_ptr 不再比原来的DiskJobThread 更可复制和移动,不过...... @KerrekSB 是的,但如果目标只是稍后初始化,我认为应该没问题;o -1:问题不在于初始化std::thread(因为它使用了一个很好的移动分配操作),所以这不能回答问题。 @Angew 好的,我已将答案更新为至少提及问题的第二部分。 @MohammadAliBaydoun 您的答案的问题是它根本不需要 - 按值存储 std::thread 非常好,它确实可以稍后初始化,就像 OP 正在做的那样。将其存储在 unique_ptr 中只会通过动态分配/解除分配和间接引入效率低下。【参考方案2】:

您的问题是其他问题-您将MyClass 的实例传递给线程,而不是成员函数期望的指向MyClass 的指针。只需像这样更改DoDiskJobThread()(不要取消引用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);
    

您收到错误是因为*this 导致尝试将MyClass 复制到线程函数中,并且您的类的复制ctor 被删除(因为std::thread 的复制ctor 被删除)。但是,成员函数CopyThreadMoveThread 无论如何都需要一个指针作为第一个(隐藏)参数。

Live demonstration

【讨论】:

+1,是的,我没有注意到他通过*this而不是this,你是对的;| '=' 运算符不允许将新线程对象分配给 DiskJobThread 变量。我收到此错误:error C2678: binary '=' : no operator found which takes a left-hand operand of type 'const std::thread' (or there is no acceptable conversion) @hkBattousai 您发布的代码中没有const std::thread(您可以在ideone上看到我的更改有效)。发布您的实际代码。 认为您可以使用稍微更晦涩的方法传递std::ref(*this),但这会非常愚蠢。【参考方案3】:

线程对象创建后不能初始化;根据定义,初始化发生在创建对象时。但是你可以使用swap 将一个线程对象移动到另一个对象中:

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

【讨论】:

它不起作用。抛出此错误:C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory0(611): error C2280: 'std::thread::thread(const std::thread &)' : 试图引用已删除的函数 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\thread(70) : 请参阅“std::thread::thread”的声明 1> 此诊断发生在生成的编译器中函数'Abc::Abc(const Abc &) @codeLover -- 问题出在你的类 Abc 中,它显然有一个 std::thread 作为数据成员。std::thread 不可复制,所以你的类必须禁用复制构造或者有一个复制构造函数,以某种方式尝试复制该线程对象。【参考方案4】:

我的目标是保留std::thread 对象作为数据成员,并在需要时对其进行初始化。

由于默认构造的std::thread 对象没有关联的执行线程,您可以通过使用这样的对象作为(移动)赋值操作的目标来实现这一点。不过要注意,下面的不是初始化,而是赋值

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

使用std::thread(func) 创建的临时std::thread 对象具有关联的执行线程。该执行线程的所有权通过移动分配转移给th——即th从临时线程中窃取了该执行线程的所有权。

请注意,如果在分配时th 有一个关联的执行线程,则会调用std::terminate()

【讨论】:

以上是关于是否可以定义一个 std::thread 并稍后对其进行初始化?的主要内容,如果未能解决你的问题,请参考以下文章

如何知道分离的 std::thread 是不是已完成执行?

std::thread 导致应用程序中止并出现错误 R6010

使用多线程加速(std::async、std::thread 还是?)

mingw 5 std::this_thread 未定义

std::this_thread::sleep_for - 上下文切换到这个线程吗?

分离的 std::thread 终止后是不是需要删除?