【问题标题】:std::thread::detach causes crash after original caller is destroyedstd::thread::detach 在原始调用者被销毁后导致崩溃
【发布时间】:2013-09-03 14:35:59
【问题描述】:
struct Test {
    bool active{true};

    void threadedUpdate() {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        if(!active) // crashes here after Test instance is destroyed
            return; 
    }

    Test() { 
        std::thread([this]{ while(true) threadedUpdate(); }).detach();
    }

    ~Test() { 
        // somehow stop the detached thread?
    } 
};

当Test 的实例被初始化时,它会生成并分离一个在后台运行的std::thread。当同一个实例被销毁时,前面提到的线程会尝试访问 active 成员,该成员与实例一起被销毁,导致崩溃(以及 AddressSanitizer 回溯)。

有没有办法停止~Test() 上的分离线程?

设计很糟糕。 在调用者被销毁之前在后台运行的线程应该如何正确生成/处理?

【问题讨论】:

  • 作为一般规则,您不应该分离线程,除非该线程控制它正在使用的任何资源的生命周期。该线程使用Test,但不控制其生命周期,因此不应分离。

标签: c++ multithreading c++11 detach stdthread


【解决方案1】:

使线程成为类的成员,而不是在构造函数中分离它,而是在析构函数中加入它。要停止线程循环,您可以在类中使用一个布尔值来指示线程是否应该继续运行 (std::atomic<bool> update)。

线程可能正在执行这个:[this] { while (update) threadUpdate(); }。

在你的类的析构函数中,执行update = false,然后调用thread.join()

【讨论】:

  • @VittorioRomeo - 否则你需要一个互斥锁来保护它。根据标准,如果至少有一个访问是写入,非atomic 类型在没有显式同步的情况下并发访问是永远不会安全的。
  • 另一个问题:使用成员 std::future 并使用 std::async 启动函数会更干净吗?我想它不需要显式连接。
  • 它可以减少人为错误的可能性(例如忘记加入析构函数中的线程),所以它可能是值得的。
【解决方案2】:

您无法停止分离的线程。这就是.detach() 的意义所在——你没有任何 方法来引用分离的线程,至少就C++ 标准所指定的而言。如果要保留线程句柄,请将std::thread 存储在析构函数中并调用.join()。

【讨论】:

  • 真的有必要在析构函数中调用.join()而不是while(true)我使用while(active)并在析构函数中设置active = false;吗?那不应该停止线程吗?
  • 另一个问题:使用成员std::future<void> 并使用std::async 启动函数会更干净吗?我想它不需要显式连接。
  • 可能它会起作用,但通常最干净的解决方案是在析构函数中释放 所有 资源,即使是那些将异步释放的资源。这大概需要调用join。
猜你喜欢
  • 2013-01-19
  • 1970-01-01
  • 1970-01-01
  • 2021-10-04
  • 1970-01-01
  • 1970-01-01
  • 2020-02-13
  • 2021-01-07
相关资源
最近更新 更多