C语言中该原子引用计数优化实现是否安全?
代码安全性分析:跨线程引用计数实现
给定前提条件
- Header_mark_as_shared仅在对象跨线程前调用,无论其处于哪个线程;
- Header_mark_as_shared的首次调用始终在单线程上下文执行;
- 后续调用Header_mark_as_shared时,读取到的is_shared值始终为1。
待分析代码
#include <stdint.h> typedef struct Header { uint8_t is_shared; uint64_t reference_count; } Header; void Header_mark_as_shared( Header * const self ) { if (!self->is_shared) { ++self->reference_count; self->is_shared = 1; } else { __atomic_fetch_add(&self->reference_count, 1, __ATOMIC_RELAXED); } } void Header_acquire( Header * const self ) { if (!self->is_shared) { ++self->reference_count; } else { __atomic_fetch_add(&self->reference_count, 1, __ATOMIC_RELAXED); } } uint8_t Header_release( Header * const self ) { if (!self->is_shared) { return !--self->reference_count; } else { return __atomic_fetch_sub(&self->reference_count, 1, __ATOMIC_ACQ_REL) == 1; } } uint8_t Header_is_one( Header const * const self ) { if (!self->is_shared) { return self->reference_count == 1; } else { return __atomic_load_n(&self->reference_count, __ATOMIC_ACQUIRE) == 1; } }
安全性结论:符合前提条件下完全安全
各函数安全分析
Header_mark_as_shared
- 首次调用在单线程上下文(前提2),此时
is_shared为0,执行的++reference_count和is_shared=1都是单线程操作,无任何竞态风险; - 后续调用时,根据前提3,
is_shared已固定为1,会进入原子递增分支,__atomic_fetch_add保证了引用计数修改的原子性,不会出现多线程下的计数错误。
- 首次调用在单线程上下文(前提2),此时
Header_acquire
- 当
is_shared=0时,对象处于单线程环境,直接递增计数完全安全,无并发竞争; - 当
is_shared=1时,对象已跨线程,原子递增操作确保多线程下计数的正确性,__ATOMIC_RELAXED内存序足够满足计数递增的需求(无需额外内存同步,仅需原子性)。
- 当
Header_release
- 单线程阶段(
is_shared=0):直接递减计数安全,返回值准确反映计数是否归零; - 多线程阶段(
is_shared=1):__atomic_fetch_sub搭配__ATOMIC_ACQ_REL内存序,既保证减法操作的原子性,又确保修改对其他线程可见,同时通过返回值正确判断是否为最后一次释放(原计数为1时意味着释放后归零)。
- 单线程阶段(
Header_is_one
- 单线程阶段:直接读取计数判断是否为1,无竞争,结果可靠;
- 多线程阶段:
__atomic_load_n加__ATOMIC_ACQUIRE内存序,确保读取到的是最新的计数值,避免缓存不一致导致的错误判断。
前提条件的核心作用
- 前提1确保
is_shared一旦变为1,所有后续操作都进入原子分支,彻底隔离单线程与多线程操作的交叉风险; - 前提2规避了首次设置
is_shared时的竞态; - 前提3避免了后续调用
Header_mark_as_shared时可能出现的分支判断竞态。
内容的提问来源于stack exchange,提问作者João Pires
相关产品推荐
相关产品推荐

