不同硬件原子操作(如__sync_fetch_and_add与__sync_lock_test_and_set)是否互斥?
好问题!这其实触及了硬件原子操作的核心特性——所有针对同一内存地址的原子操作,无论具体类型是CAS、FAA(Fetch-And-Add)还是Test-And-Set,都会被硬件层面保证互斥访问。下面结合你的代码和问题详细解释:
核心结论
针对同一共享变量的不同原子操作(比如你代码里的__sync_lock_test_and_set和__sync_fetch_and_add),是完全互斥的。当一个线程执行getAndSet(),另一个线程执行getAndAdd()时,硬件会确保这两个操作不会同时对value_进行修改,不会出现竞态条件导致的中间状态泄漏。
背后的原理
原子操作的本质是把“读-改-写”(或类似的复合操作)变成一个不可分割的硬件指令。为了实现这一点,CPU会通过两种机制保证同一内存地址的原子操作串行化:
- 总线锁:当执行原子操作时,CPU会锁定系统总线,阻止其他CPU访问该内存地址,直到当前原子操作完成。
- 缓存锁:如果目标内存地址已经被缓存在CPU的L1/L2缓存中,CPU会利用缓存一致性协议(如MESI)锁定缓存行,避免其他CPU对该缓存行的修改,从而更高效地实现原子性。
不管是哪种原子操作(CAS、FAA、Test-And-Set),只要它们操作的是同一个内存地址,硬件就会自动触发上述机制,保证这些操作互斥执行。
结合你的代码例子分析
看你实现的AtomicIntegerT类:
template<typename T> class AtomicIntegerT { public: AtomicIntegerT() : value_(0) { } T get() { return __sync_val_compare_and_swap(&value_, 0, 0); } T getAndAdd(T x) { return __sync_fetch_and_add(&value_, x); } T addAndGet(T x) { return getAndAdd(x) + x; } T getAndSet(T newValue) { return __sync_lock_test_and_set(&value_, newValue); } private: volatile T value_; };
这里的几个方法:
get()用CAS(__sync_val_compare_and_swap)实现原子读取(因为CAS的“比较-交换”操作如果新旧值相同,本质就是原子读)getAndAdd()用FAA(__sync_fetch_and_add)实现原子加法getAndSet()用Test-And-Set(__sync_lock_test_and_set)实现原子赋值
当多个线程同时调用这些方法时,硬件会保证所有针对value_的原子操作都串行执行,不会出现两个操作同时修改value_的情况。比如线程A执行getAndSet(5)的同时,线程B执行getAndAdd(3),最终value_的结果要么是8(A先执行,B在5的基础上加3),要么是5(B先执行后,A直接覆盖为5)——但不管顺序如何,两个操作都是完整执行的,不会出现中间的错误值。
补充:volatile的作用
你代码里的value_用了volatile修饰,这里要注意:volatile本身不提供原子性,它的作用是告诉编译器不要对该变量的读写进行优化,确保每次读写都直接访问内存(而不是寄存器缓存)。真正的原子性是由__sync_*系列函数对应的硬件原子指令保证的,volatile只是配合原子操作,避免编译器优化破坏原子操作的正确性。
内容的提问来源于stack exchange,提问作者cserbo

