原子参考计数 [英] Atomic Reference Counting
问题描述
我试图了解线程安全的原子引用计数是如何工作的,例如 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>
- 线程A :atomically将引用减少为
0
。 - 线程B :将引用计数原子地增加到
1
。 - / 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 plusdelete
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
.- Thread A: atomically decrefs the refcount to
0
. - Thread B: atomically increfs the refcount to
1
. - Thread A: calls
delete
on the managed object pointer. - 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 sameshared_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 thingshared_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_ptr
in a threadsafe manner, you have to ensure that all loads and stores happen viastd::atomic_...
operations which are specialized forshared_ptr
.这篇关于原子参考计数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- Thread A: atomically decrefs the refcount to
< <>线程A :在托管对象指针上调用
delete
。