用户实现的原子操作能否建立同步关系?自旋锁实现可行性探讨
先看用户实现的自旋锁伪代码:
struct SpinLock{ bool* ptr; void lock(){ // mov eax, false // new_val = true while /* lock cmpxchg [ptr], new_val */{ // until *ptr == false } } void unlock(){ // mov [ptr], false } };
根据C++标准[atomics.order] p2的规定:
对原子对象M执行release操作的原子操作A,与对M执行acquire操作且获取A领头的释放序列中任何副作用值的原子操作B同步。
你提出的判断是对的:这个自定义实现里确实没法利用release和acquire的语义——因为这俩是编译器能识别的特殊内存序标记,而你的代码只是手写了汇编指令,编译器不会把这些指令和内存序语义关联起来。这会导致线程中的unlock操作无法和另一个线程最新的lock操作建立happen-before关系,锁本该提供的内存可见性保障就失效了。
针对你问的两个问题,具体解答如下:
1. 是否无法通过用户实现的原子对象来实现自旋锁?
不是完全不行,但必须让编译器感知到内存序语义,不能只靠裸汇编。
你可以借助编译器提供的内置原子操作函数(比如GCC的__atomic_compare_exchange_n、MSVC的_InterlockedCompareExchange等),在调用这些函数时指定acquire/release语义。这些内置函数是编译器原生支持的,会生成正确的内存屏障指令,同时建立对应的happen-before关系。如果只是手写裸汇编的lock cmpxchg和普通mov,编译器可能会对锁前后的代码做重排序优化,破坏锁的内存可见性,这样的自旋锁在多线程环境下是不安全的。
2. 是否无法自行实现ordering_acquire和ordering_release,因为编译器不会识别它们?
是的,你没法自己定义出和std::memory_order_acquire/std::memory_order_release等价的语义标记。因为这些标准内存序不是普通的枚举值,它们是编译器内置的特殊标识,会直接影响代码生成和优化策略:
- 使用标准内存序时,编译器会在合适位置插入内存屏障,同时禁止特定的代码重排序;
- 自定义的标记编译器完全不识别,不会触发任何内存序相关的限制或屏障插入,自然达不到同样的效果。
当然你可以封装编译器的内置原子操作,模拟出类似的接口,但核心还是依赖编译器提供的底层支持,而非完全自行实现内存序语义。
内容的提问来源于stack exchange,提问作者xmh0511

