为何C++标准库shared_ptr递减原子引用计数使用acq_rel内存序?
为什么递增用relaxed内存序?
- 引用计数递增的核心需求只是保证计数本身的原子性,不需要同步其他内存状态。当拷贝shared_ptr时,控制块里的对象指针、析构函数等关键数据在创建时就已经完全初始化,新的shared_ptr实例不需要通过递增计数来同步这些数据。
- relaxed是最轻量级的内存序,不会插入任何内存屏障,仅保证原子操作的正确性,能最大化提升高频拷贝操作的性能。
为什么递减用acq_rel内存序?
- 递减操作的核心风险是计数归零时要销毁对象和控制块,这时候必须满足两个同步要求:
- acquire语义:确保当前线程中所有对共享对象的读写操作,在执行销毁动作前都已完成并可见,避免销毁时对象仍被使用。
- release语义:确保计数递减到0的结果能被其他线程及时看到,防止其他线程访问已销毁对象,同时避免多线程重复触发销毁逻辑(双重释放)。
- acq_rel内存序同时满足这两个需求,既保证当前线程操作的完成性,又保证计数状态的全局可见性。
为什么不采用“递增release、递减acquire”的组合?
- 递增用release完全冗余:release语义会强制后续内存操作不能重排到递增之前,但拷贝shared_ptr后的操作(比如访问对象)根本不需要这种强约束——控制块的初始化早于任何拷贝操作,release只会额外插入内存屏障,徒增性能开销。
- 递减用acquire不足以保证安全:acquire仅能保证当前线程看到之前的操作,但无法确保其他线程及时感知计数变为0的状态。多线程同时递减时,可能出现多个线程都认为自己是最后持有者,重复执行销毁逻辑,引发未定义行为。
内容的提问来源于stack exchange,提问作者meisel
相关产品推荐
相关产品推荐

