c++ - Effective Modern C++ 书中 ThreadRAII 的实现是否正确?

标签 c++ c++11 c++14

书中的第 37 项说明了 ThreadRAII 的实现,它可以加入线程或在线程被销毁时将其分离。通过声明析构函数,编译器不会生成移动操作,但在书中,作者说没有理由不能移动它们,并说编译器会生成正确的移动操作,并建议我们使用 '= default ' 实现。

#include <thread>

class ThreadRAII
{
public:
    enum class DtorAction { join, detach };

    ThreadRAII(std::thread&& t, DtorAction a)
        : action(a)
        , t(std:: move(t))
    {}

    ~ThreadRAII()
    {
        if(t.joinable())
        {
            if(action == DtorAction::join)
                t.join();
            else
                t.detach();
        }
    }

    ThreadRAII(ThreadRAII&&) = default;

    ThreadRAII& operator=(ThreadRAII&&) = default;

    std::thread& get() { return t; }

private:
    DtorAction action;
    std::thread t;
};

int main()
{
    ThreadRAII t{std::thread{[]{}}, ThreadRAII::DtorAction::join};

    t = ThreadRAII{std::thread{[]{}}, ThreadRAII::DtorAction::detach};

    return 0;
}

但在上面的例子中,调用了 std::terminate。

我认为默认的移动构造函数应该没问题,但移动赋值不行,因为移动赋值必须在获取新资源之前释放当前资源。否则,赋值将破坏可连接的线程,从而导致程序终止。

我没有在本书的勘误表中看到这个问题。这本书说默认的移动赋值运算符应该没问题真的错了吗?我想确定并让其他人查看它以便与作者联系。

我认为应该是这样的:

#include <thread>

class ThreadRAII
{
public:
    enum class DtorAction { join, detach };

    ThreadRAII(std::thread&& t, DtorAction a)
        : action(a)
        , t(std:: move(t))
    {}

    ~ThreadRAII()
    {
        release();
    }

    ThreadRAII(ThreadRAII&&) = default;

    ThreadRAII& operator=(ThreadRAII&& rhs)
    {
        release();
        action = rhs.action; 
        t = std::move(rhs.t);
        return *this;
    }

    std::thread& get() { return t; }

    void release()
    {
        if(t.joinable())
        {
            if(action == DtorAction::join)
                t.join();
            else
                t.detach();
        }
    }

private:
    DtorAction action;
    std::thread t;
};

int main()
{
    ThreadRAII t{std::thread{[]{}}, ThreadRAII::DtorAction::join};

    t = ThreadRAII{std::thread{[]{}}, ThreadRAII::DtorAction::detach};

    return 0;
}

最佳答案

是的,这是一个明确的 3(5) 违规规则。

一旦您有了自定义的 dtor,您就必须同样自定义您的特殊赋值和构造函数。在这里,销毁的线程导致连接或分离而分配给不连接的事实相对不一致。

作者大概想过assigning-to-empty的情况,算出来还好,没想到assigning-to-engaged。

关于c++ - Effective Modern C++ 书中 ThreadRAII 的实现是否正确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74084172/

相关文章:

c++ - 使用类指针拆分项目

C++ 模板 : override some, 但不是全部,默认参数?

c++ - constexpr 表达式和变量生命周期,g++ 和 clang 不一致的例子

c++ - 如果方法的调用者不需要数据的所有权,有什么好的方法可以避免复制?

c++ - 如何生成具有推导签名的成员函数

c++ - 迭代 Eigen 中的对称稀疏矩阵

c++ - 带类型限制的完美转发

c++ - C++中静态常量成员变量与收缩转换的关系

c++ - Boost property_tree错误:在.ini文件中获取元素时,数据转换为 “j”类型失败

c++ - 将 lambda 隐式转换为函数 ptr 以创建类