调用具有条件变量等待的线程对象的析构函数时会发生啥?

Posted

技术标签:

【中文标题】调用具有条件变量等待的线程对象的析构函数时会发生啥?【英文标题】:What happens when calling the destructor of a thread object that has a condition variable waiting?调用具有条件变量等待的线程对象的析构函数时会发生什么? 【发布时间】:2012-12-21 03:45:33 【问题描述】:

我正在使用SynchronisedQueue 在线程之间进行通信。我发现当附加线程正在等待条件变量时销毁线程对象会导致程序崩溃。这可以通过在线程销毁之前调用detach() 来纠正。但我想知道当等待条件变量的线程终止时会发生什么。有没有其他方法可以使用条件变量来避免这种情况?

#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

template <typename Type> class SynchronisedQueue 
 public:
  void Enqueue(Type const & data) 
    std::unique_lock<std::mutex> lock(mutex_);
    queue_.push(data);
    condition_.notify_one();
  
  Type Dequeue() 
    std::unique_lock<std::mutex> lock(mutex_);
    while (queue_.empty())
      condition_.wait(lock);
    Type result = queue_.front();
    queue_.pop();
    return result; 
  
 private:
  std::queue<Type> queue_;
  std::mutex mutex_;
  std::condition_variable condition_; 
;

class Worker 
public:
  Worker(SynchronisedQueue<int> * queue) : queue_(queue) 
  void operator()() 
    queue_->Dequeue();    // <-- The thread waits here.
  
private:
  SynchronisedQueue<int> * queue_;
;

int main() 
  auto queue = new SynchronisedQueue<int>();
  Worker worker(queue);
  std::thread worker_thread(worker);
  worker_thread.~thread();  // <-- Crashes the program.
  return 0;

【问题讨论】:

【参考方案1】:

来自 C++11 规范:

30.3.1.3 线程析构函数 [thread.thread.destr] ~thread();

如果 joinable(),则调用 std::terminate()。否则,没有效果。

[ 注意:在其析构函数中隐式分离或加入 joinable() 线程可能导致仅在引发异常时遇到难以调试的正确性(分离)或性能(加入)错误。因此,程序员必须确保在线程仍可连接时永远不会执行析构函数。 ——尾注]

因此调用thread destructor 而不先调用join(等待它完成)或detach 保证立即调用std::terminate 并结束程序。

【讨论】:

我测试过了。你说的对。如果工作线程是可连接的并且正在运行,你甚至不能让主线程返回。所以我猜这个答案(***.com/questions/12207684/…)是错误的,因为 std::terminate() 或 ~thread() 当线程可连接和运行时会导致程序而不是线程终止。 @WiSaGaN:如果您阅读了答案,这就是答案的实际含义。 好的。我看到“但他们终止了每个线程”。而joinable() 是检查您是否可以安全地调用~thread() 的唯一标准。即使线程已经完成了线程体,也必须先调用joinable()detach() 才能安全地销毁线程对象。 你能详细说明为什么标准是这样完成的。为什么不在 d-tor 中调用 join() @Nick:引用的注释说明了为什么这样做。【参考方案2】:

std::thread 的析构函数将调用std::terminate,如果它在线程上运行,如果你没有调用join()(等待线程完成)或detach()(从对象分离线程)就可以了。

您的代码调用了worker_thread 的析构函数,而没有调用join()detach(),因此调用了std::terminate。这与条件变量的存在无关。

【讨论】:

【参考方案3】:

当某物正在或可能正在使用资源时,您永远不能破坏资源。这真的只是常识

【讨论】:

以上是关于调用具有条件变量等待的线程对象的析构函数时会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

为啥自动对象的析构函数被调用两次?

当删除没有虚拟析构函数的多态对象时会发生啥?

c++ 析构函数 是在啥时候执行

可连接 std::thread 的析构函数

C++ 虚拟析构函数 (virtual destructor)

笔记-线程安全的生命期管理