c++ - 为什么 std::shared_ptr 两次调用我的析构函数?
问题描述
在这个程序中,为什么第 14 行的析构函数会为同一个 mystruct_t 实例调用两次?
我假设这个程序中的所有指针操作都是线程安全的。我认为原子更新不适用于我的系统或编译器。我在 MSVC 2017、MSVC 2019 和 clang 上试过这个
/* This crashes for me (line 19) */
#include <iostream>
#include <vector>
#include <thread>
#include <memory>
#include <chrono>
#include <assert.h>
struct mystruct_t {
int32_t nInvocation = 0;
~mystruct_t();
mystruct_t() = default;
};
mystruct_t::~mystruct_t() {
nInvocation++;
int nInvoke = nInvocation;
if (nInvoke > 1) {
/* destructor was invoked twice */
assert(0);
}
/* sleep is not necessary for crash */
//std::this_thread::sleep_for(std::chrono::microseconds(525));
}
std::shared_ptr<mystruct_t> globalPtr;
void thread1() {
for (;;) {
std::this_thread::sleep_for(std::chrono::microseconds(1000));
std::shared_ptr<mystruct_t> ptrNewInstance = std::make_shared<mystruct_t>();
globalPtr = ptrNewInstance;
}
}
void thread2() {
for (;;) {
std::shared_ptr<mystruct_t> pointerCopy = globalPtr;
}
}
int main()
{
std::thread t1;
t1 = std::thread([]() {
thread1();
});
std::thread t2;
t2 = std::thread([]() {
thread2();
});
for (int i = 0;; ++i) {
std::this_thread::sleep_for(std::chrono::microseconds(1000));
std::shared_ptr<mystruct_t> pointerCopy = globalPtr;
globalPtr = nullptr;
}
return 0;
}
解决方案
正如这里的几个用户已经提到的那样,您遇到了未定义的行为,因为您在全局(或外线程)更改您的引用对象,而线程本地,您尝试复制分配它。sharedPtr 的一个缺点,特别是对于新手来说,是建议中相当隐蔽的危险,你在复制它们时总是线程安全的。由于您不使用引用,因此这可能会变得更加难以怀疑。始终尝试首先将 shared_ptr 视为常规类,并具有常见的(“微不足道的”)成员复制分配,其中在不受保护的线程环境中总是可能发生干扰。
如果您将来会遇到使用该代码或类似代码的类似情况,请尝试使用强大的基于通道广播/事件的方案,而不是本地放置的锁!通道(缓冲或基于单个数据)本身关心适当的数据生命周期,确保 sharedPtr 的基础数据“救援”。
推荐阅读
- python - 无法在组件 0 中批处理具有不同形状的张量
- php - 仅在前端更改 Datepicker 日期格式
- javascript - 如何使用 javascript 创建用于阻止和存储搜索到的 URL 的扩展
- reactjs - 在 react js Material-ui 中删除芯片
- javascript - I got the error "TypeError: products.map is not a function"
- python - python ray没有完成所有远程调用
- java - 带有 Firestore 的 TabLayout
- java - 我的示例是 ADT 的描述吗?(爪哇)
- c - 用 C 编写一个程序,提示用户输入两个日期,然后指示日历上哪个日期更早
- php - 用户注册后不保持登录状态