我应该删除内部创建线程的类的复制构造函数和赋值运算符吗?

w12*_*128 4 c++ multithreading thread-safety copy-constructor

当一个类拥有线程和互斥对象时,是否存在任何潜在的危险情况,其中复制/赋值将是危险的,这意味着应该删除复制构造函数和赋值?

考虑以下示例代码:

class A : B
{
    std::thread t;
    std::mutex m;

  public:
    A() : B() {}
    virtual ~A()
    {
        if (t.joinable())
            t.join();
    }

    // Should I delete cctor and assignment operator?

    virtual void Method()
    {
        t = std::thread([this] 
        {
            std::lock_guard<std::mutex> lck(m);
            ... // processing
        });
    }

    // Other methods that lock on mutex m
};
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,创建的线程Method()将不会在外部可见A,这意味着使用默认cctor进行复制不应该有问题,因为整个状态将被复制.我的推理是否正确?

Yak*_*ont 5

任何其(扩展)状态包含指针的类必须具有已删除的副本/移动或必须编组该状态.

t = std::thread([this] 
Run Code Online (Sandbox Code Playgroud)

上面的行存储指向this类的扩展状态的指针.

因此,默认复制和移动是不合适的.删除是一个选项; 仔细地,可能昂贵地编组另一个.

此外,线程和互斥体都只是移动类型.因此将隐式删除副本.

但是,您的移动分配/构造将由编译器编写并且错误.所以删除它们或修复它们.


没有类值类型(如Java/C#)的语言有一个成语,它有一个带状态的类和一个处理状态的线程.这是一个像C++这样以价值为中心的语言的糟糕计划.将您的状态存储在外部(比如,共享或唯一的ptr),与您的线程共享(作为共享ptr或观察ptr),突然默认移动是有意义的.

如果不这样做,你的对象变得无序 - 无法安全移动 - 这会削弱许多优秀的C++习语,或者迫使外部智能指针包装.