原子引用计数 [英] Atomic Reference Counting

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

问题描述

我试图确切地了解线程安全的原子引用计数是如何工作的,例如与 std::shared_ptr 一样.我的意思是,基本概念很简单,但我真的很困惑 decref 加 delete 如何避免竞争条件.

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.

这个来自Boost的教程演示如何使用 Boost 原子库(或 C++11 原子库)实现原子线程安全引用计数系统.

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:

假设引用计数当前为 1.

Say the refcount is currently 1.

  1. 线程 A:原子地将引用计数减少到 0.
  2. 线程 B:将引用计数原子地增加到 1.
  3. 线程 A:在托管对象指针上调用 delete.
  4. 线程 B:将引用计数视为 1,访问托管对象指针...SEGFAULT!
  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!

我无法理解是什么阻止了这种情况的发生,因为在引用计数达到 0 和对象被删除之间的 之间没有什么可以阻止数据竞争.减少引用计数和调用 delete 是两个独立的非原子操作.那么没有锁怎么可能呢?

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?

推荐答案

您可能高估了 shared_ptr 提供的线程安全性.

You probably overestimate the threadsafety a shared_ptr provides.

原子引用计数的本质是确保如果shared_ptr(管理相同对象)的两个不同实例被访问/修改,将有没有竞争条件.但是,如果两个线程访问同一个 shared_ptr 对象(其中一个是写入),则 shared_ptr 并不能确保线程安全.一个例子是例如如果一个线程取消引用指针,而另一个线程重置它.
因此,关于 shared_ptr 唯一保证的是,只要在 shared_ptr 的单个实例上没有竞争(它也不访问对象),就不会有双重删除和泄漏它指向线程安全)

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)

因此,如果没有其他线程可以同时删除/重置它,那么创建 shared_ptr 的副本也是安全的(您也可以说,它不是内部同步的).这是您描述的场景.

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

再重复一遍:从多个线程访问单个 shared_ptr 实例,其中一个访问是对指针的写入仍然是竞争条件.

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.

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

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天全站免登陆