C++ 是否共享ptr';s删除程序是否执行任何同步?

C++ 是否共享ptr';s删除程序是否执行任何同步?,c++,multithreading,synchronization,shared-ptr,atomic,C++,Multithreading,Synchronization,Shared Ptr,Atomic,使用多线程环境的每个人都知道必须在线程之间进行同步以避免争用情况。我特别感兴趣的是发生在共享\u ptr deleter中的同步 在我的实际情况中,我有多个类以某种方式交互,其中一些类知道同步正在进行,而另一些类则不知道。在这个例子中,我人为地将它们捆绑在一个对象上,以阐明这个问题: class TestObject { public: TestObject() : mMarked(false) { } ~TestObje

使用多线程环境的每个人都知道必须在线程之间进行同步以避免争用情况。我特别感兴趣的是发生在共享\u ptr deleter中的同步

在我的实际情况中,我有多个类以某种方式交互,其中一些类知道同步正在进行,而另一些类则不知道。在这个例子中,我人为地将它们捆绑在一个对象上,以阐明这个问题:

class TestObject
{
    public:
        TestObject()
        : mMarked(false)
        { }

        ~TestObject()
        {
            // use of mMarked here indicates that the destructor must be synchronized
            // with any thread that calls mark()
            std::cout << "Object " << (mMarked ? "was marked." : "was not marked.");
        }

        void mark()  { mMarked = true; }

        void someBehaviorThatDoesntNeedSynchronization();

    private:
        bool    mMarked;
};


thread 1:
std::shared_ptr<TestObject> objPtr1 = /* initialize to some instance */;
objPtr1->someBehaviorThatDoesntNeedSynchronization();
objPtr1.reset(); // may call the destructor

thread 2:
std::shared_ptr<TestObject> objPtr2 = /* initialize to the same instance */;
objPtr2->mark();
objPtr2.reset(); // may call the destructor
类TestObject
{
公众:
TestObject()
:m标记(错误)
{ }
~TestObject()
{
//此处使用mMarked表示必须同步析构函数
//使用任何调用mark()的线程
std::cout-mark();
objPtr2.reset();//可以调用析构函数
规范似乎暗示根本不存在同步。然而,这似乎是非常不考虑因素的。似乎线程1在拥有调用析构函数的特权之前就应该知道对象发生的所有同步(如果在堆栈展开期间调用析构函数,这可能会很残酷)

我遗漏了什么吗?我知道每个shared_ptr的实现实际上都是因为这个原因而进行同步的,但是我在规范中找不到任何东西表明我可以信任它

规范中是否有任何内容建议在调用删除程序之前进行同步?

来自:

所有成员函数(包括复制构造函数和复制赋值)可以由共享\u ptr的不同实例上的多个线程调用,而无需额外同步,即使这些实例是同一对象的副本和共享所有权。如果多个执行线程不同步地访问同一共享\u ptr,并且其中任何一个访问都使用共享\u ptr的非常量成员函数,则dat如果发生竞争,可以使用原子函数的共享_ptr重载来防止数据竞争


因此,
std::shared_ptr
的同步有限。使用非
const
方法(包括析构函数)在多个线程中不能安全访问
shared_ptr
的单个实例

但是两个共享的shared_ptr,它们共享相同的生存期,并且可以从不同的线程安全地访问数据

如何实现这一点的一个想法是销毁执行一个联锁递减,这会减少参考计数器,并返回它原子化地减少的值

然后,无论哪个
~shared\u ptr
(或
.reset()
)将其减少到
0
,都会删除该对象

如果我们假设
objPtr1
objPtr2
是从同一个源正确构造的(可能是通过同步,或者在传递给工作线程之前在同一个线程中),并且在
.reset()时
被称为所有其他
共享\u ptr
,除了这两个已超出范围之外,然后这两个
中的一个
。reset()
将销毁
测试对象

现在,从技术上讲,
mMarked
的值是同步的,当一个线程在另一个线程(可能)之前以非同步方式修改该值时读取它,结果是未定义的行为。这方面的一个实际示例是由两个CPU分别缓存的
mMarked
。在一种情况下,缓存的副本被修改。这与另一个缓存不同步,而另一个缓存恰好在
.reset()
上获得销毁


因此,简言之,在多个线程中访问指向同一对象本身的不同
共享\u ptr
是安全的,但对共享对象的访问并不同步。

当共享\u ptr对象释放其对指针的保持时,它会自动减少引用计数。如果该引用计数现在为零,则释放为ing shared_ptr是引用该对象的唯一实例,它调用deleter


因此,对共享所有权对象的销毁是通过引用计数上的原子操作来同步的。

虽然我看到的实现都像您建议的那样使用原子,但奇怪的是,规范除了一个对数据竞争的引用之外,没有提及原子或同步。@Cort:atomics可能会包装本机平台impl校勘和标准库的其他部分可以直接使用该本机实现,而无需经过包装器。@CortAmmon显然不必使用原子。如果您讨厌效率,您更喜欢互斥体!这说明了在共享\u ptr本身上使用成员函数,但没有说明在共享\u ptr上使用成员函数他们所指向的对象。为什么您希望shared_ptr提供同步(或者更确切地说,定义同步行为)如果您不使用共享的\u ptr函数修改管理对象,您将不得不自己同步对管理对象的访问,如果这是您的实现策略。这真是一厢情愿。refcount为0的事实意味着有一个完美的机会同步NAIVE实现同步是偶然发生的,流行的优化实现故意同步。除了规范没有选择调用它之外,所有的东西都是一致的。这也意味着你必须非常小心地为异常展开堆栈,因为每个破坏共享\u ptr的堆栈展开都有可能在正在处理异常情况!如果您有两个线程同时展开堆栈,那就很麻烦了。用感觉的话来说,当共享线程在处理多个线程上的异常时,不让任何人参与共享\u ptr已经必须进行的同步似乎很残酷。@CortAmmon:不管哪个线程调用