非无锁std::atomic能否使用SeqLock替代现有自旋锁实现?
在MSVC STL和LLVM libc++的实现中,不满足原生原子大小的std::atomic都是基于自旋锁实现的。
libc++实现代码如下:
_LIBCPP_INLINE_VISIBILITY void __lock() const volatile { while(1 == __cxx_atomic_exchange(&__a_lock, _LIBCPP_ATOMIC_FLAG_TYPE(true), memory_order_acquire)) /*spin*/; } _LIBCPP_INLINE_VISIBILITY void __lock() const { while(1 == __cxx_atomic_exchange(&__a_lock, _LIBCPP_ATOMIC_FLAG_TYPE(true), memory_order_acquire)) /*spin*/; }
MSVC实现代码如下:
inline void _Atomic_lock_acquire(long& _Spinlock) noexcept { #if defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) // 算法来自2020年5月发布的《英特尔64和IA-32架构优化参考手册》 // 示例2-4 带递增退避的竞争锁优化版本,第2-22页 // 上述手册中的代码采用0BSD许可证发布 int _Current_backoff = 1; const int _Max_backoff = 64; while (_InterlockedExchange(&_Spinlock, 1) != 0) { while (__iso_volatile_load32(&reinterpret_cast<int&>(_Spinlock)) != 0) { for (int _Count_down = _Current_backoff; _Count_down != 0; --_Count_down) { _mm_pause(); } _Current_backoff = _Current_backoff < _Max_backoff ? _Current_backoff << 1 : _Max_backoff; } } #elif /* ... */ #endif }
我在思考更优实现方案时产生了两个疑问:
- 能否用SeqLock替代上述自旋锁实现,优势是读写无冲突时读操作成本极低
- SeqLock能否结合OS等待机制优化,比如当读者观测到奇数序列号时,通过原子等待底层机制(Linux
futex/WindowsWaitOnAddress)等待,避免自旋锁的饥饿问题
我个人认为该方案是可行的:虽然当前C内存模型未覆盖SeqLock,但std::atomic要求承载类型必须可平凡拷贝,因此seqlock中使用memcpy进行读写是可行的,只要搭配足够的内存屏障实现类似volatile的效果且不会过度影响优化即可。而且这属于特定C实现的头文件内部逻辑,不需要满足跨平台可移植要求。
现有C++实现SeqLock的相关参考问答:
- 用32位原子实现64位原子计数器
- 如何用C++11原子库实现seqlock
内容的提问来源于stack exchange,提问作者Alex Guteniev
相关产品推荐
相关产品推荐

