在破坏调用期间从另一个线程未定义的行为调用对象上的方法?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在破坏调用期间从另一个线程未定义的行为调用对象上的方法?相关的知识,希望对你有一定的参考价值。

在析构函数调用期间从另一个线程未定义的行为调用对象上的方法(我保证所需的字段仍然存活且可访问并且同步访问它们)?

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:此外,虚拟方法也出现了同样的问题?是否有可能调度虚拟调用以覆盖派生类中的方法(哪些数据已被销毁)?根据上面的引用,它不应该。但是我仍然不确定是否可以将它应用于多线程场景。

答案

大多数情况下,是的,它是未定义的行为,非常不安全。

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

但它仍然是不安全的,你应该尽可能地尝试防止它发生。

以上是关于在破坏调用期间从另一个线程未定义的行为调用对象上的方法?的主要内容,如果未能解决你的问题,请参考以下文章

当 MainWindow 从另一个类(不在主函数中)启动时,QT ui 未显示

不调用对象的析构器是未定义的行为吗?

C# 从另一个线程调用 form.show()

从另一个异步方法调用的 Spring 异步方法

在 IIS 服务器上的 PUT API 调用期间未触发 Application_BeginRequest

如何从另一个线程的 cpp 代码同步调用 qml 信号处理程序?