【问题标题】:Is it safe to hold a std::lock_guard in the destructor?在析构函数中保存 std::lock_guard 是否安全?
【发布时间】:2019-09-20 09:58:54
【问题描述】:

我正在尝试确定以下代码是否安全,或者它是否是 UB 并且仅在这种情况下运行良好(运行它here):

#include <iostream>
#include <mutex>

struct Foo
{
  std::mutex mutex;
  ~Foo()
  {
    std::lock_guard<std::mutex> lock(mutex);
  }
};

int main() 
{
  {
    Foo foo;
  }
  std::cout << "everything seems to work fine...?" << std::endl;
}

具体来说,我们是否保证在析构函数中定义的局部变量会在成员变量之前被销毁?

我从cppreference.com 找到了以下内容,但它似乎没有完全回答我的问题:

销毁顺序

无论是用户定义的还是隐式定义的析构函数,在析构函数体执行完毕后,编译器都会为类的所有非静态非变体成员调用析构函数,按照声明的相反顺序,然后调用所有直接非虚拟基类的析构函数以相反的构造顺序(依次调用其成员及其基类的析构函数等),然后,如果该对象属于大多数派生类,则调用析构函数所有虚拟基地。

【问题讨论】:

    标签: c++ multithreading destructor


    【解决方案1】:

    根据[class.dtor]/9中的标准,

    在执行析构函数的主体并销毁主体内分配的所有自动对象后, 类 X 的析构函数调用 X 的直接非变量非静态数据成员的析构函数,即析构函数 对于X 的非虚拟直接基类,如果X 是最派生类(15.6.2)的类型,则它的析构函数 为X 的虚拟基类调用析构函数。 ...

    这肯定回答了你的问题。

    【讨论】:

      【解决方案2】:

      析构函数的主体在任何成员被析构之前执行。从这个意义上说,它是安全的。

      但在问题是否安全之前,必须询问是否有任何合理的用例将互斥锁保存在析构函数(以及构造函数)中。

      只有一个线程可以构造或破坏一个对象。这必须通过对象外部的互斥体来保证,例如在工厂中构造时或在共享指针中销毁时。

      【讨论】:

      • 也许这正是这个类正在做的——管理一个需要同步的子对象。
      • @Lightness Races in Orbit:子对象永远不会被访问,除非从其父对象访问。因此不需要持有对象的互斥体,因为必须确保在构造或销毁时只有一个线程访问该对象。
      • 子对象可能已经暴露在外部,即使没有,持有互斥锁对于管理封装子对象的关闭也很重要。
      • @Lightness Races in Orbit:访问受正在破坏的互斥体保护的暴露子对象是可疑的。从实际的角度来看,我会在代码审查时拒绝它。
      • 它没有被破坏。还没有。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-07-08
      • 1970-01-01
      • 2019-07-23
      • 1970-01-01
      • 1970-01-01
      • 2013-12-25
      • 2015-08-19
      相关资源
      最近更新 更多