当前位置: 首页 > news >正文

Async++ 源码分析10--ref_count.h

一、Async++ 代码目录结构

Async++ 项目的目录结构清晰,主要包含根目录下的配置文件、源代码目录、头文件目录以及示例代码目录,具体结构如下:

asyncplusplus/
├── .gitignore               # Git 忽略文件配置
├── Async++Config.cmake.in   # CMake 配置模板文件
├── CMakeLists.txt           # CMake 构建脚本
├── LICENSE                  # 许可证文件(MIT 许可证)
├── README.md                # 项目说明文档
├── examples/                # 示例代码目录
│   └── gtk_scheduler.cpp    # GTK 调度器示例
├── src/                     # 源代码目录
│   ├── fifo_queue.h         # FIFO 队列实现
│   ├── internal.h           # 内部头文件(包含类型定义、宏等)
│   ├── scheduler.cpp        # 调度器实现
│   ├── singleton.h          # 单例模式实现
│   ├── task_wait_event.h    # 任务等待事件实现
│   ├── threadpool_scheduler.cpp  # 线程池调度器实现
│   └── work_steal_queue.h   # 工作窃取队列实现
└── include/                 # 头文件目录├── async++.h            # 主头文件(对外提供统一接口)└── async++/             # 子模块头文件目录├── aligned_alloc.h├── cancel.h├── continuation_vector.h├── parallel_for.h├── parallel_invoke.h├── parallel_reduce.h├── partitioner.h    # 分区器相关定义├── range.h          # 范围(迭代器对)相关定义├── ref_count.h├── scheduler.h      # 调度器接口定义├── scheduler_fwd.h├── task.h           # 任务类定义├── task_base.h      # 任务基类定义├── traits.h└── when_all_any.h

二、ref_count.h源码分析

2.1 源码

// Copyright (c) 2015 Amanieu d'Antras
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.#ifndef ASYNCXX_H_
# error "Do not include this header directly, include <async++.h> instead."
#endifnamespace async {
namespace detail {// Default deleter which just uses the delete keyword
template<typename T>
struct default_deleter {static void do_delete(T* p){delete p;}
};// Reference-counted object base class
template<typename T, typename Deleter = default_deleter<T>>
struct ref_count_base {std::atomic<std::size_t> ref_count;// By default the reference count is initialized to 1explicit ref_count_base(std::size_t count = 1): ref_count(count) {}void add_ref(std::size_t count = 1){ref_count.fetch_add(count, std::memory_order_relaxed);}void remove_ref(std::size_t count = 1){if (ref_count.fetch_sub(count, std::memory_order_release) == count) {std::atomic_thread_fence(std::memory_order_acquire);Deleter::do_delete(static_cast<T*>(this));}}void add_ref_unlocked(){ref_count.store(ref_count.load(std::memory_order_relaxed) + 1, std::memory_order_relaxed);}bool is_unique_ref(std::memory_order order){return ref_count.load(order) == 1;}
};// Pointer to reference counted object, based on boost::intrusive_ptr
template<typename T>
class ref_count_ptr {T* p;public:// Note that this doesn't increment the reference count, instead it takes// ownership of a pointer which you already own a reference to.explicit ref_count_ptr(T* t): p(t) {}ref_count_ptr(): p(nullptr) {}ref_count_ptr(std::nullptr_t): p(nullptr) {}ref_count_ptr(const ref_count_ptr& other) LIBASYNC_NOEXCEPT: p(other.p){if (p)p->add_ref();}ref_count_ptr(ref_count_ptr&& other) LIBASYNC_NOEXCEPT: p(other.p){other.p = nullptr;}ref_count_ptr& operator=(std::nullptr_t){if (p)p->remove_ref();p = nullptr;return *this;}ref_count_ptr& operator=(const ref_count_ptr& other) LIBASYNC_NOEXCEPT{if (p) {p->remove_ref();p = nullptr;}p = other.p;if (p)p->add_ref();return *this;}ref_count_ptr& operator=(ref_count_ptr&& other) LIBASYNC_NOEXCEPT{if (p) {p->remove_ref();p = nullptr;}p = other.p;other.p = nullptr;return *this;}~ref_count_ptr(){if (p)p->remove_ref();}T& operator*() const{return *p;}T* operator->() const{return p;}T* get() const{return p;}T* release(){T* out = p;p = nullptr;return out;}explicit operator bool() const{return p != nullptr;}friend bool operator==(const ref_count_ptr& a, const ref_count_ptr& b){return a.p == b.p;}friend bool operator!=(const ref_count_ptr& a, const ref_count_ptr& b){return a.p != b.p;}friend bool operator==(const ref_count_ptr& a, std::nullptr_t){return a.p == nullptr;}friend bool operator!=(const ref_count_ptr& a, std::nullptr_t){return a.p != nullptr;}friend bool operator==(std::nullptr_t, const ref_count_ptr& a){return a.p == nullptr;}friend bool operator!=(std::nullptr_t, const ref_count_ptr& a){return a.p != nullptr;}
};} // namespace detail
} // namespace async

        这段代码是 Async++ 框架中侵入式引用计数智能指针的核心实现,位于 async::detail:: 命名空间,包含两个核心组件:ref_count_base(引用计数基类)和 ref_count_ptr(侵入式智能指针)。其核心功能是通过 “对象自身存储引用计数” 的方式,实现动态内存的安全管理,避免内存泄漏和野指针问题,广泛用于框架中任务对象(如 task_base)的生命周期管理。以下是分层解析:

2.2. 核心概念铺垫

引用计数智能指针分为两类,Async++ 实现的是侵入式(Intrusive) 智能指针:

  • 侵入式:引用计数存储在对象内部(通过继承 ref_count_base 实现),对象本身感知引用计数变化;
  • 非侵入式:引用计数存储在智能指针内部(如 std::shared_ptr),对象无需修改,智能指针单独管理计数。

侵入式的优势是内存开销低(无需额外为计数分配内存)、对象复用性强(适合框架中频繁创建 / 销毁的任务对象),但要求对象必须继承自引用计数基类。

2.3. 核心组件解析

代码分为 “引用计数基类” 和 “智能指针封装” 两层,基类负责计数管理,智能指针负责提供友好的接口并自动维护计数。

(1)引用计数基类:ref_count_base

ref_count_base<T, Deleter> 是模板基类,供框架中的对象(如任务对象 task_base)继承,内部存储原子引用计数,并提供计数增减、对象销毁等核心方法。

模板参数说明
参数名作用
T子类类型(用于 static_cast 正确销毁对象,需满足 “子类继承自 ref_count_base<T>”)
Deleter销毁器(默认 default_deleter<T>,用 delete 销毁对象,支持自定义销毁逻辑)
核心成员与方法
template<typename T, typename Deleter = default_deleter<T>>
struct ref_count_base {// 原子引用计数:确保多线程下计数修改的线程安全std::atomic<std::size_t> ref_count;// 1. 构造函数:默认初始化引用计数为 1(对象创建时,初始拥有 1 个引用)explicit ref_count_base(std::size_t count = 1) : ref_count(count) {}// 2. 增加引用计数(线程安全)void add_ref(std::size_t count = 1) {// 内存序:relaxed(仅需保证计数原子性,无其他内存依赖)ref_count.fetch_add(count, std::memory_order_relaxed);}// 3. 减少引用计数,计数为 0 时销毁对象(核心方法)void remove_ref(std::size_t count = 1) {// 步骤1:原子减少计数,返回减少前的旧值// 内存序:release(确保对象销毁前,所有对该对象的修改已完成)if (ref_count.fetch_sub(count, std::memory_order_release) == count) {// 步骤2:若旧值 == 减少的计数 → 计数变为 0,需要销毁对象// 内存序:acquire(确保销毁前,所有线程对对象的访问已结束)std::atomic_thread_fence(std::memory_order_acquire);// 步骤3:调用销毁器删除对象(默认用 delete,支持自定义)Deleter::do_delete(static_cast<T*>(this));}}// 4. 无锁增加计数(仅在已加锁场景使用,非线程安全)void add_ref_unlocked() {// 先加载计数,加 1 后存储(无原子操作,需外部保证线程安全)ref_count.store(ref_count.load(std::memory_order_relaxed) + 1, std::memory_order_relaxed);}// 5. 判断是否为唯一引用(用于优化,如“唯一引用时可修改对象”)bool is_unique_ref(std::memory_order order) {return ref_count.load(order) == 1;}
};
关键逻辑:remove_ref 的线程安全销毁

remove_ref 是最核心的方法,确保 “仅当最后一个引用被释放时,才销毁对象”,且多线程安全:

  • 原子操作:用 std::atomic::fetch_sub 原子减少计数,避免多线程下计数计算错误;
  • 内存序
    • release:确保当前线程对对象的所有修改,在对象销毁前已被其他线程可见;
    • acquire:确保销毁对象时,其他线程对对象的所有访问已结束;
  • 销毁器:通过 Deleter::do_delete 销毁,支持自定义(如对象在共享内存中,需特殊释放逻辑)。
默认销毁器:default_deleter

default_deleter<T> 是默认销毁器,仅封装 delete 操作,适用于大多数场景:

template<typename T>
struct default_deleter {static void do_delete(T* p) {delete p; // 用 delete 销毁对象(需确保对象由 new 分配)}
};
(2)侵入式智能指针:ref_count_ptr

ref_count_ptr<T> 是模板智能指针,封装了 ref_count_base 子类的指针,提供与 std::shared_ptr 类似的接口(如 operator*operator->),并自动维护引用计数(构造时加计数,析构时减计数)。

核心设计原则
  • 所有权传递:通过构造 / 赋值操作,自动传递对象的引用权,避免手动调用 add_ref/remove_ref
  • 线程安全:依赖 ref_count_base 中的原子计数,确保多线程下智能指针操作的安全性;
  • 零额外开销:仅存储一个裸指针(T* p),无其他成员,内存开销与裸指针一致。
核心方法解析
template<typename T>
class ref_count_ptr {T* p; // 指向 ref_count_base<T> 子类的裸指针public:// 1. 构造函数:// - 裸指针构造:不增加计数(需用户保证已拥有引用权,如对象刚 new 出时)explicit ref_count_ptr(T* t) : p(t) {}// - 默认构造:空指针,计数无意义ref_count_ptr() : p(nullptr) {}// - 拷贝构造:增加计数(共享所有权)ref_count_ptr(const ref_count_ptr& other) LIBASYNC_NOEXCEPT : p(other.p) {if (p) p->add_ref(); // 其他指针非空,增加当前对象的引用计数}// - 移动构造:不增加计数(转移所有权,原指针置空)ref_count_ptr(ref_count_ptr&& other) LIBASYNC_NOEXCEPT : p(other.p) {other.p = nullptr; // 原指针失去所有权,避免析构时重复减计数}// 2. 赋值运算符:// - 拷贝赋值:先减当前计数(若非空),再共享新对象并加计数ref_count_ptr& operator=(const ref_count_ptr& other) LIBASYNC_NOEXCEPT {if (p) { p->remove_ref(); p = nullptr; } // 释放当前对象p = other.p;if (p) p->add_ref(); // 共享新对象,加计数return *this;}// - 移动赋值:先减当前计数,再转移新对象所有权(原指针置空)ref_count_ptr& operator=(ref_count_ptr&& other) LIBASYNC_NOEXCEPT {if (p) { p->remove_ref(); p = nullptr; }p = other.p;other.p = nullptr;return *this;}// - 空指针赋值:释放当前对象,置空ref_count_ptr& operator=(std::nullptr_t) {if (p) { p->remove_ref(); p = nullptr; }return *this;}// 3. 析构函数:自动减少计数,计数为 0 时销毁对象~ref_count_ptr() {if (p) p->remove_ref();}// 4. 指针操作接口(与裸指针兼容)T& operator*() const { return *p; }    // 解引用T* operator->() const { return p; }    // 成员访问T* get() const { return p; }           // 获取裸指针T* release() {                         // 释放所有权(不减少计数)T* out = p; p = nullptr; return out;}// 5. 类型转换与比较explicit operator bool() const { return p != nullptr; } // 判空// 与其他智能指针/空指针比较(基于裸指针地址)friend bool operator==(const ref_count_ptr& a, const ref_count_ptr& b) { return a.p == b.p; }friend bool operator!=(const ref_count_ptr& a, const ref_count_ptr& b) { return a.p != b.p; }
};

2.4. 框架中的典型用法

Async++ 中,ref_count_ptr 主要用于管理任务对象(如 task_base 的生命周期,因为任务可能被多个线程引用(如主线程提交任务、线程池执行任务、其他任务依赖该任务),需要安全的引用计数管理。

示例:任务对象继承 ref_count_base
// 1. 任务基类:继承自 ref_count_base,拥有引用计数
class task_base : public async::detail::ref_count_base<task_base> {
public:virtual ~task_base() = default;virtual void execute() const = 0; // 任务执行接口
};// 2. 具体任务类:继承自 task_base
class PrintTask : public task_base {
public:void execute() const override {std::cout << "Task executed\n";}
};// 3. 用 ref_count_ptr 管理任务生命周期
int main() {// 步骤1:创建任务对象,初始引用计数为 1task_base* raw_task = new PrintTask();// 步骤2:用 ref_count_ptr 接管,此时计数仍为 1(裸指针构造不增加计数)async::detail::ref_count_ptr<task_base> task_ptr(raw_task);// 步骤3:拷贝智能指针,计数增加到 2async::detail::ref_count_ptr<task_base> task_ptr2 = task_ptr;// 步骤4:多线程共享任务(无需手动管理计数)std::thread t([task_ptr2]() {task_ptr2->execute(); // 线程安全访问任务});t.join();// 步骤5:智能指针析构时自动减少计数// - task_ptr2 析构:计数从 2 → 1// - task_ptr 析构:计数从 1 → 0,任务对象被 delete 销毁return 0;
}

2.5. 关键设计亮点

(1)线程安全的计数管理

通过 std::atomic<std::size_t> 存储引用计数,add_ref/remove_ref 用原子操作确保多线程下计数修改的正确性,避免 “计数竞态” 导致的内存泄漏或重复销毁。

(2)内存高效
  • 侵入式设计:计数存储在对象内部,无需额外分配内存(对比 std::shared_ptr 需额外分配 “控制块” 存储计数);
  • 智能指针轻量:ref_count_ptr 仅存储一个裸指针,大小与裸指针一致(32 位系统 4 字节,64 位系统 8 字节)。
(3)灵活的销毁策略

支持自定义 Deleter,可应对特殊场景(如:

  • 对象在栈上:Deleter 为空操作,不调用 delete
  • 对象在共享内存:Deleter 调用共享内存释放接口。
(4)兼容裸指针接口

提供 get()/release()/operator*/operator-> 等接口,用法与裸指针、std::shared_ptr 一致,降低用户学习成本。

5. 注意事项

(1)对象必须继承自 ref_count_base

ref_count_ptr<T> 仅能管理继承自 ref_count_base<T> 的对象,否则调用 add_ref/remove_ref 会编译失败(类型不匹配)。

(2)裸指针构造的安全性

用裸指针 T* 构造 ref_count_ptr 时,必须确保该裸指针指向的对象引用计数已初始化为 1(如通过 new 创建的 ref_count_base 子类),否则会导致计数错误(如计数为 0 时,remove_ref 会立即销毁对象)。

(3)避免循环引用

与 std::shared_ptr 类似,ref_count_ptr 也会产生循环引用问题(如 A 引用 B,B 引用 A,两者计数永远不为 0,导致内存泄漏),需通过 “弱引用”(框架未实现,需手动设计)避免。

3. 总结

Async++ 的 ref_count_base 和 ref_count_ptr 是侵入式引用计数智能指针的高效实现,核心价值在于:

  • 为框架中的任务对象提供线程安全的生命周期管理;
  • 内存开销低、接口友好,兼容 C++ 标准智能指针的使用习惯;
  • 支持自定义销毁策略,适配不同内存分配场景。

该实现是框架并发安全的基础之一,确保多线程下任务对象的创建、共享、销毁不会出现内存问题,同时兼顾性能与灵活性。

http://www.dtcms.com/a/450099.html

相关文章:

  • 单页面竞价网站网站+建设设计
  • 基于MATLAB的物理层算法原型验证
  • PHP网站开发程序员招聘一站式做网站哪家专业
  • 绵阳网站建设哪家好微信下拉小程序怎么关闭
  • 软件设计师——08 算法设计与分析
  • 炫酷企业网站网上买东西有哪些平台
  • DAY 42 Grad-CAM与Hook函数-2025.10.6
  • 绵阳网站建设培训学校隐私空间
  • 淮安网站建设做北京电梯招标的网站
  • 专业企业网站建设定制百度如何做网站
  • Net-Tools工具包详解:Linux网络管理经典工具集
  • 极路由做网站无锡网站推广公司排名
  • registrateAPI——非空函数
  • 环境设计案例网站基于html5动画的网站
  • CCF编程能力等级认证GESP—C++4级—20250927
  • 网站收录率怎样建立自己网站多少钱
  • 电商平台网站设计公司企业建站搭建
  • 【数据结构】链栈的基本操作
  • 实战分享:股票数据API接口在量化分析中的应用与体验
  • 个人建设网站还要备案么wordpress建站详细教程视频
  • Vue2 和 Vue3 View
  • 乐趣做网站厦门做网站的公司
  • 使用jmeter做压力测试
  • [工作流节点15] 推送消息节点在企业内部通知中的应用实践
  • 热转印 东莞网站建设ui界面设计英文
  • 【数据结构学习篇】--树
  • Linux中驱动程序通过fasync异步通知应用程序的实现
  • MySQL索引优化:让查询快如闪电
  • 什么是营销型网站呢什么网站做新产品代理
  • 海沧建设网站多少jetpack报错 wordpress