欢迎光临
我们一直在努力

线程销毁前必须不可结合

好的,我们来解析这个条款的核心内容:

条款37的核心观点

该条款强调:当 std::thread 对象被销毁时,它必须处于不可结合的状态。否则,程序的行为是未定义的(通常会导致 std::terminate 被调用,程序终止)。

关键概念

  • 可结合状态:当一个 std::thread 对象关联着一个底层系统线程(即该线程正在运行、可运行或等待运行)时,它处于可结合状态。
  • 不可结合状态:当一个 std::thread 对象没有关联任何底层线程时,它处于不可结合状态。这发生在:
    • 默认构造后(未关联线程)。
    • 被 join() 后(线程执行完毕,关联解除)。
    • 被 detach() 后(关联被显式解除)。
    • 被移动赋值后(原对象不再关联线程)。
    • 被移动构造后(新对象获得关联,原对象不再关联)。
  • 为何需要确保在所有路径上不可结合?

    std::thread 的析构函数行为取决于其状态:

    • 如果析构时处于不可结合状态:析构函数正常执行,无问题。
    • 如果析构时处于可结合状态:析构函数会调用 std::terminate(),终止整个程序。

    因此,我们必须确保在 std::thread 对象的生命周期结束前(即离开其作用域或被销毁前),通过 join() 或 detach() 将其变为不可结合状态。这包括所有可能的执行路径,例如:

    • 正常执行路径。
    • 函数提前返回(如 return, break, continue)。
    • 抛出异常。

    如何实现?

  • 显式管理(风险较高):在每个可能的退出点(包括异常处理)之前,手动调用 join() 或 detach()。这种方式容易遗漏路径,导致错误。
  • RAII(推荐方式):创建一个资源管理类,在其析构函数中处理线程的 join() 或 detach()。这是确保在所有路径(包括异常)上都正确清理线程资源的标准做法。
  • // 一个简单的RAII线程包装器示例
    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();
    }
    }
    }

    std::thread& get() { return t; } // 提供访问原始线程的接口(谨慎使用)

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

    // 使用示例
    void someFunction() {
    ThreadRAII tr(
    std::thread([] { /* 线程任务 */ }),
    ThreadRAII::DtorAction::join // 或 detach
    );
    // … 函数逻辑,可能抛出异常或提前返回
    } // 无论函数如何退出,tr的析构函数都会确保线程变为不可结合

    https://weibo.com/tv/show/1034:5275497211297795
    https://weibo.com/tv/show/1034:5275497186394137
    https://weibo.com/tv/show/1034:5275497173811211
    https://weibo.com/tv/show/1034:5275497161228298
    https://weibo.com/tv/show/1034:5275497136062479
    https://weibo.com/tv/show/1034:5275497131606019
    https://weibo.com/tv/show/1034:5275497102508043
    https://weibo.com/tv/show/1034:5275497094119437
    https://weibo.com/tv/show/1034:5275497052176402
    https://weibo.com/tv/show/1034:5275497052176400

    重要注意事项

    • joinable() 检查:在调用 join() 或 detach() 前,必须检查线程是否处于可结合状态。对一个不可结合的线程调用这些函数是未定义行为。
    • join vs detach:选择 join 还是 detach 取决于业务逻辑。join 等待线程结束,确保其任务完成;detach 让线程在后台独立运行,主线程不再等待和管理它。
    • 移动语义:std::thread 是可移动但不可复制的。ThreadRAII 的构造函数使用了 std::move 来获取线程的所有权。

    总结

    Effective Modern C++ 条款37的核心是强制要求程序员管理好 std::thread 对象的生命周期,确保它们在销毁前变为不可结合状态,以避免程序意外终止。使用RAII技术是遵循这条建议、写出健壮多线程代码的最佳实践。

    赞(0)
    未经允许不得转载:171主机测评 » 线程销毁前必须不可结合
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址