Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ fetch_sub真的是原子的吗?_C++_Multithreading_Reference Counting_Stdatomic - Fatal编程技术网

C++ fetch_sub真的是原子的吗?

C++ fetch_sub真的是原子的吗?,c++,multithreading,reference-counting,stdatomic,C++,Multithreading,Reference Counting,Stdatomic,我有以下代码(用C++编写): StringRef类中的代码: inline void retain() const { m_refCount.fetch_add(1, std::memory_order_relaxed); } inline void release() const { if(m_refCount.fetch_sub(1, std::memory_order_release) == 1){ std:

我有以下代码(用C++编写):

StringRef类中的代码:

    inline void retain() const {
        m_refCount.fetch_add(1, std::memory_order_relaxed);
    }
    inline void release() const {
        if(m_refCount.fetch_sub(1, std::memory_order_release) == 1){
            std::atomic_thread_fence(std::memory_order_acquire);
            deleteFromParent();
        }
    }
InternetString中的代码:

public:
    inline InternedString(){
        m_ref = nullptr;
    }
    inline InternedString(const InternedString& other){
        m_ref = other.m_ref;
        if(m_ref)
            m_ref->retain();
    }
    inline InternedString(InternedString&& other){
        m_ref = other.m_ref;
        other.m_ref = nullptr;
    }
    inline InternedString& operator=(const InternedString& other){
        if(&other == this)
            return *this;
        if(other.m_ref)
            other.m_ref->retain();
        if(m_ref)
            m_ref->release();
        m_ref = other.m_ref;
        return *this;
    }
    inline InternedString& operator=(InternedString&& other){
        if(&other == this)
            return *this;
        if(m_ref)
            m_ref->release();
        m_ref = other.m_ref;
        other.m_ref = nullptr;
        return *this;
    }
    /*! @group Destructors */
    inline ~InternedString(){
        if(m_ref)
            m_ref->release();
    }
private:
    inline InternedString(const StringRef* ref){
        assert(ref);
        m_ref = ref;
        m_ref->retain();
    }
当我在多个线程中执行此代码时,会对同一对象多次调用deleteFromParent()。我不明白为什么。。。即使我过度释放,我也不应该有这种行为,我想


有人能帮我吗?我做错了什么?

fetch\u sub
是尽可能原子化的,但这不是问题所在

尝试修改代码,如下所示:

    if(m_refCount.fetch_sub(1, std::memory_order_release) == 1){
        Sleep(10);
        std::atomic_thread_fence(std::memory_order_acquire);
        deleteFromParent();
看看会发生什么

如果您的析构函数被使用
InternedString
运算符的线程抢占,它们将愉快地在不知不觉中获得对即将删除的对象的引用。
这意味着您的代码的其余部分可以自由引用已删除的对象,从而导致各种UBs,包括可能重新增加您的完美原子引用计数,从而导致多个完美原子破坏

假设任何人都可以在不首先锁定析构函数的情况下复制引用,这显然是错误的,而且如果你把它埋在教科书中,那么只会让它变得更糟,因为需要一大堆操作符来对最终用户隐藏引用变戏法

如果任何任务都可以随时删除您的对象,则需要一些代码,如
InternedString a=b
将无法知道
b
是否为有效对象。
只有在对象确实有效时设置了所有引用,引用计数机制才会按预期工作。
您可以做的是在代码段中创建任意数量的
InternedString
s,在代码段中不能并行删除(无论是在init期间还是通过普通的互斥锁),但一旦析构函数处于空闲状态,这就可以作为参考

在不使用互斥体或其他同步对象的情况下,使其工作的唯一方法是添加一种获取引用的机制,该机制将让用户知道对象已被删除。这是

现在,如果您试图将其全部隐藏在由五个运算符组成的规则下,唯一剩下的解决方案是在
InternedString
中添加某种
有效的
属性,在尝试访问底层字符串之前,每一位代码都必须进行检查

这相当于将多任务问题抛到界面最终用户的桌面上,在最好的情况下,最终用户会使用互斥来防止其他代码位从他脚下删除对象,或者只是修补代码,直到隐式同步明显解决了问题,在应用程序中植入这么多滴答作响的定时炸弹


原子计数器和/或结构不能替代多任务同步。除了一些能够设计超智能算法的专家外,原子变量只是一个被大量语法糖衣包裹的巨大陷阱。

我解决了这个问题。。。问题是我在复活一个濒临死亡的物体。由于删除和恢复由相同的旋转锁保护,因此发生的情况是对象被恢复,然后又被删除,因为它的引用计数达到零。我解决了这个问题,只需在锁定自旋锁后删除对象时测试ref计数是否为零。非常感谢。请注意旋转锁定可能会导致严重的性能损失。你可以考虑使用一个普通的互斥体来代替。如果您在运行测试程序时同时尝试这两种方法并查看性能表,您可能会注意到互斥锁导致的CPU消耗大大减少。当然,这完全取决于测试程序如何激发代码。