是在销毁期间从另一个线程的未定义行为调用对象上的方法吗?

时间:2018-09-13 16:11:57

标签: c++

是否正在从另一个线程未定义的行为进行析构函数调用期间在对象上调用方法(我保证必填字段仍然有效并且可访问,并且对它们的访问已同步)?

c++14 draft standard(12.7.4)说:

  

可以调用成员函数,包括虚拟函数(10.3)   在建造或破坏期间(12.6.2)。当虚函数   直接从构造函数或间接调用   破坏者,包括在建造或销毁   类的非静态数据成员以及调用的对象   适用于正在建造或销毁的物体(称为x),   所调用的函数是构造函数或   析构函数的类,而不是在派生更高的类中覆盖它的。

试图了解一种模式,对象A拥有线程B,线程B可以随时调用对象A上的回调是否有效。对象A的析构函数将在破坏任何相关状态之前加入线程。

相关代码示例:

#include <vector>
#include <thread>
#include <atomic>
#include <iostream>

struct A {
  void reg() {
    thread_ = std::thread([this]() {
      while (a_ < 10) {
        pr();
      }
    });
  }

  void pr() {
    std::unique_lock<std::mutex> lock(mt_);
    std::cout << "Hello World\n";
    a_++;
  }

  ~A() {
    std::unique_lock<std::mutex> lock(mt_);
    std::cout << "Destruction started\n";
    lock.unlock();

    thread_.join();
  }

  int a_{0};
  std::mutex mt_;
  std::thread thread_;
};

int main() {
  A a;
  a.reg();
}

PS:我知道我需要同步对字段的访问,并在离开析构函数主体后要小心停止回调。

PPS:另外,对于虚拟方法也有同样的问题吗?是否可以将虚拟调用分派到派生类中的重写方法(哪些数据已被破坏)?根据上面的引用,它不应该。但是我仍然不确定我们能否将其应用于多线程方案。

1 个答案:

答案 0 :(得分:1)

大多数时候,是的,这是不确定的行为,非常不安全。

一个值得注意的例外是,如果所讨论的方法不需要读取或写入对象的状态(virtual方法算作“需要读取状态”,即使该方法本身不需要读取或写入状态)。如果是这种情况,则在其上调用该方法不会导致未定义的行为。

尽管如此,它仍然是不安全的,您应该尽可能地尝试以防止它发生。