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

Is calling a method on an object during destruction call from another thread undefined behaviour?

本文关键字:调用 是否 方法 未定义 对象 线程 另一个      更新时间:2023-10-16

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

C ++ 14草案标准(12.7.4(说:

成员函数,包括虚函数 (10.3(,可以调用 在施工或销毁期间(12.6.2(。当虚拟函数 直接或间接从构造函数或从 析构函数,包括在建造或销毁期间 类的非静态数据成员,以及调用的对象 应用是正在建造或破坏的对象(称为X(, 调用的函数是构造函数 or 中的最终覆盖器 析构函数的类,而不是在派生更多的类中重写它的类。

尝试了解对象 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 Worldn";
a_++;
}
~A() {
std::unique_lock<std::mutex> lock(mt_);
std::cout << "Destruction startedn";
lock.unlock();
thread_.join();
}
int a_{0};
std::mutex mt_;
std::thread thread_;
};
int main() {
A a;
a.reg();
}

PS:我知道我需要同步对字段的访问,并注意在离开析构函数正文后停止回调。

PPS:另外,虚拟方法也存在同样的问题?是否可以将虚拟调用调度到派生类中的重写方法(数据已被销毁(?根据上面的引用,它不应该。但我仍然不确定我们是否可以将其应用于多线程场景。

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

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

不过,它仍然是不安全的,您应该尽可能多地防止它发生。