为什么 std::shared_ptr 两次调用我的析构函数?

Why does std::shared_ptr call my destructor twice?

在这个程序中,为什么第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 视为常规 class,并使用常见的 ('trivial') 成员复制分配,其中在不受保护的线程环境中始终可能发生干扰。

如果您将来会遇到类似情况或类似代码,请尝试使用基于通道 broadcasting/event 的稳健方案,而不是本地放置的锁!通道(缓冲的或基于单一数据的)本身关心适当的数据生命周期,确保 sharedPtr 的基础数据 'rescue'.