原子参考计数 [英] Atomic Reference Counting

查看:224
本文介绍了原子参考计数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解线程安全的原子引用计数是如何工作的,例如 std :: shared_ptr 。我的意思是,基本的概念很简单,但我真的很困惑,如何使用减法加删除避免竞争条件。



Boost 教程演示了如何使用Boost原子库(或C ++ 11原子库)实现原子线程安全引用计数系统。

  #include< boost / intrusive_ptr.hpp> 
#include< boost / atomic.hpp>

class X {
public:
typedef boost :: intrusive_ptr< X>指针;
X():refcount_(0){}

private:
mutable boost :: atomic< int> refcount_;
friend void intrusive_ptr_add_ref(const X * x)
{
x-> refcount_.fetch_add(1,boost :: memory_order_relaxed);
}
friend void intrusive_ptr_release(const X * x)
{
if(x-> refcount_.fetch_sub(1,boost :: memory_order_release)== 1){
boost :: atomic_thread_fence(boost :: memory_order_acquire);
delete x;
}
}
};

好吧,所以我得到了一般的想法。但我不明白为什么下面的情况是不可能的:



说引用计数目前是 1 。 / p>


  1. 线程A :atomically将引用减少为 0

  2. 线程B :将引用计数原子地增加到 1

  3. < <>线程A :在托管对象指针上调用 delete
  4. / em>:查看引用计数 1 ,访问托管对象指针... SEGFAULT! ol>

    我不明白阻止这种情况发生的原因,因为在引用计数达到0之前,没有什么能阻止之间的数据竞争。该对象被删除。解释引用计数并调用 delete 是两个单独的非原子操作。

    解决方案

    你可能高估了shared_ptr提供的线程安全性。



    原子计数的本质是确保如果两个不同实例 shared_ptr (管理同一对象)被访问/修改,将不会有竞争条件。但是, shared_ptr 不能确保线程安全,如果两个线程访问相同的 shared_ptr 对象写)。一个例子是如果一个线程解引用指针,而另一个线程重置它。

    所以关于 shared_ptr gurantees的唯一事情是,不会有双删除和泄漏,只要没有在一个shared_ptr的单个实例上的竞争(它也不会访问对象,它指向线程安全)



    结果,也创建一个shared_ptr的副本只是种族免费,如果没有其他线程,可以逻辑删除/重置它在同一时间(你也可以说,它不是内部同步)。



    重复一次:访问单个 shared_ptr 多个线程中的实例,其中这些访问之一是写入(指针)仍然是竞争条件



    如果你想要eg以线程安全的方式复制 std :: shared_ptr ,您必须确保所有加载和存储都通过 std :: atomic _... 专门用于 shared_ptr


    I'm trying to understand exactly how thread-safe, atomic reference counting works, for example as with std::shared_ptr. I mean, the basic concept is simple, but I'm really confused about how the decref plus delete avoids race conditions.

    This tutorial from Boost demonstrates how an atomic thread-safe reference counting system can be implemented using the Boost atomic library (or the C++11 atomic library).

    #include <boost/intrusive_ptr.hpp>
    #include <boost/atomic.hpp>
    
    class X {
    public:
      typedef boost::intrusive_ptr<X> pointer;
      X() : refcount_(0) {}
    
    private:
      mutable boost::atomic<int> refcount_;
      friend void intrusive_ptr_add_ref(const X * x)
      {
        x->refcount_.fetch_add(1, boost::memory_order_relaxed);
      }
      friend void intrusive_ptr_release(const X * x)
      {
        if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1) {
          boost::atomic_thread_fence(boost::memory_order_acquire);
          delete x;
        }
      }
    };
    

    Okay, so I get the general idea. But I don't understand why the following scenario is NOT possible:

    Say the refcount is currently 1.

    1. Thread A: atomically decrefs the refcount to 0.
    2. Thread B: atomically increfs the refcount to 1.
    3. Thread A: calls delete on the managed object pointer.
    4. Thread B: sees the refcount as 1, accesses the managed object pointer... SEGFAULT!

    I can't understand what prevents this scenario from occurring, since there is nothing preventing a data race from between the time the refcount reaches 0, and the object is deleted. Decrefing the refcount and calling delete are two separate, non-atomic operations. So how is this possible without a lock?

    解决方案

    You probably overestimate the threadsafety a shared_ptr provides.

    The essence of atomic ref counting is to ensure that if two different instances of a shared_ptr (that are managing the same object) are accessed/modified, there will be no race condition. However, shared_ptr doesn't ensure thread safety, if two threads access the same shared_ptr object (and one of them is a write). One example would be e.g. if one thread dereferences the pointer, while the other resets it.
    So about the only thing shared_ptr gurantees is that there will be no double delete and no leak as long as there is no race on a single instance of a shared_ptr (It also doesn't make accesses to the object it points to threadsafe)

    As a result, also creating a copy of a shared_ptr is only race free, if there is no other thread, that could logically delete/reset it at the same time (you could also say, it is not internally synchronized). This is the scenario you describe.

    To repeat it once more: Accessing a single shared_ptr instance from multiple threads where one of those accesses is a write (to the pointer) is still a race condition.

    If you want to e.g. copy a std::shared_ptrin a threadsafe manner, you have to ensure that all loads and stores happen via std::atomic_... operations which are specialized for shared_ptr.

    这篇关于原子参考计数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆