为何Glibc保证原子性的int与sig_atomic_t多线程自增结果不符?
多线程自增计数异常的根源:原子访问≠原子操作
你的猜测完全正确——单个int或sig_atomic_t的单次读/写操作是原子的,但++自增并不是单个原子操作,这就是计数结果低于预期的核心原因。
为什么自增会出问题?
++x这类自增操作本质上是三个独立步骤的组合:
- 从内存读取变量的当前值到CPU寄存器
- 寄存器中的值执行加1操作
- 将新值写回内存
在多线程环境下,这三个步骤之间完全可能被其他线程打断:比如线程A刚读完cnt=5,还没完成加1写回,线程B也读取了cnt=5,两个线程各自加1后写回,最终cnt的值会是6而非预期的7。这种竞态条件会导致大量计数丢失,最终结果远低于100000。
逐个分析你的变量:
- 普通
int cnt:Glibc文档中说的int原子性,仅针对单次读或单次写操作(比如int temp = cnt或cnt = 100),但读-改-写的组合操作不在这个保证范围内,多线程竞态必然导致计数错误。 sig_atomic_t scnt:这个类型的设计目标是解决信号处理函数与主线程之间的原子访问问题,保证信号触发时的单次读写不会被打断,但它不提供多线程上下文下的原子操作支持。多线程间的读-改-写竞态,sig_atomic_t完全无法处理,所以结果同样错误。_Atomic int acnt:这是C标准定义的原子类型,编译器会将++acnt翻译成原子的读-改-写指令(比如x86架构下带lock前缀的inc指令),整个自增过程不会被其他线程打断,因此最终能得到准确的100000。
额外验证方案
如果要让普通int的自增操作原子化,可以使用Glibc提供的__sync_fetch_and_add函数,或者C11标准的atomic_fetch_add接口,比如:
__sync_fetch_and_add(&cnt, 1);
这样就能保证自增操作的原子性,得到正确的计数结果。
内容的提问来源于stack exchange,提问作者D.J. Elkind
相关产品推荐
相关产品推荐

