C++中TAS和CAS实现自旋锁
目录1.TAS和CAS介绍2.TAS 使用场景极简自旋锁3.CAS 核心使用场景3.1.高性能自旋锁多核首选3.2.无锁线程安全计数器不用锁纯 CAS3.3.线程安全变量更新通用值替换4.测试代码5.TAS vs CAS对比1.TAS和CAS介绍它们是CPU 硬件提供的两种最基础的原子读 - 改 - 写RMW指令专门用来解决多线程并发竞争问题是所有锁、无锁结构的底层基石。TAS Test-And-Set 「测试并设置」读取内存里的旧值强制把这个内存写成 1true返回刚才读到的旧值核心特征一定会写不管原来是什么CAS Compare-And-Swap 「比较并交换」拿内存当前值 和 你给的期望值对比如果相等才把内存改成新值如果不等什么都不做返回是否修改成功核心特征条件写入不满足就不碰内存总结TAS先写为占用再告诉你之前是不是空闲CAS先看是不是空闲是才占用不是就放弃两者都是原子操作CPU 保证整个过程一步完成不会被其他线程打断。2.TAS 使用场景极简自旋锁TAS 是 CPU 原生的最简单原子指令只能实现基础互斥自旋锁优点是代码极简缺点是多核性能差。#include atomic #include thread #include iostream //intel指令 #include immintrin.h //_mm_pause(); // 和 pause 汇编完全一样 // 自旋等待优化x86 #define asm_volatile_pause() asm volatile (pause) // -------------------------- // TAS 实现极简自旋锁 // 场景临界区极短、低并发、追求代码最简单 // -------------------------- class TasSpinLock { private: // C 原生 TAS 原子变量硬件直接支持 std::atomic_flag lock_ ATOMIC_FLAG_INIT; public: void lock() { // TAS 核心test_and_set 原子读旧值 强制写true while (lock_.test_and_set(std::memory_order_acquire)) { asm_volatile_pause(); // 自旋优化 } } void unlock() { lock_.clear(std::memory_order_release); } }; // 测试多线程计数 int cnt 0; TasSpinLock tas_lock; void work() { for (int i 0; i 100000; i) { std::lock_guardTasSpinLock guard(tas_lock); cnt; } } int main() { std::thread t1(work), t2(work); t1.join(); t2.join(); std::cout TAS 自旋锁结果: cnt std::endl; // 200000 return 0; }✅ 适合简单互斥、低并发、代码极简❌ 不适合多核高竞争缓存颠簸严重3.CAS 核心使用场景CAS 是现代并发编程的基石支持条件写入自旋时不修改共享变量多核性能远超 TAS。3.1.高性能自旋锁多核首选// -------------------------- // CAS 实现高性能自旋锁 // 场景多核高并发、低延迟临界区 // -------------------------- class CasSpinLock { private: std::atomicbool locked_{false}; public: void lock() { bool expected false; // CAS 核心只有值expected(false)才写入true while (!locked_.compare_exchange_weak( expected, true, std::memory_order_acquire )) { expected false; // 重置期望值 asm_volatile_pause(); } } void unlock() { locked_.store(false, std::memory_order_release); } };3.2.无锁线程安全计数器不用锁纯 CASCAS 可以实现完全无锁的并发操作比自旋锁更快// -------------------------- // CAS 实现无锁原子计数器 // 场景高并发计数无锁、高性能 // -------------------------- std::atomicint cas_cnt{0}; void lock_free_count() { int old_val, new_val; for (int i 0; i 100000; i) { do { old_val cas_cnt; // 读旧值 new_val old_val 1;// 计算新值 // CAS只有旧值没被修改才更新成功 } while (!cas_cnt.compare_exchange_weak(old_val, new_val)); } } // 测试 int main() { std::thread t1(lock_free_count), t2(lock_free_count); t1.join(); t2.join(); std::cout CAS 无锁计数器: cas_cnt std::endl; // 200000 return 0; }3.3.线程安全变量更新通用值替换// -------------------------- // CAS 实现安全更新共享变量 // 场景任意线程安全值修改 // -------------------------- std::atomicint value{10}; void update_value(int target) { int old value; // 只有当前值old才更新为target if (value.compare_exchange_weak(old, target)) { std::cout 更新成功\n; } else { std::cout 值已被修改更新失败\n; } }4.测试代码// C RAII 自动锁通用 template typename Lock class ScopedLock { private: Lock lock_; public: explicit ScopedLock(Lock lock) : lock_(lock) { lock_.lock(); } ~ScopedLock() { lock_.unlock(); } // 禁用拷贝 ScopedLock(const ScopedLock) delete; ScopedLock operator(const ScopedLock) delete; }; // 测试代码 int counter 0; // 二选一测试TasSpinLock 或 CasSpinLock TasSpinLock spin_lock; // CasSpinLock spin_lock; // 线程工作函数 void work() { for (int i 0; i 100000; i) { ScopedLockdecltype(spin_lock) lock(spin_lock); // 自动加锁/解锁 counter; } } int main() { // 创建两个线程竞争锁 std::thread t1(work); std::thread t2(work); t1.join(); t2.join(); // 正确结果200000 std::cout 最终计数: counter std::endl; return 0; }5.TAS vs CAS对比特性TAS 自旋锁CAS 自旋锁原子操作atomic_exchange强制写atomic_cmpxchg条件写自旋行为每次循环都修改锁变量自旋时只读不修改缓存性能差多核缓存颠簸、总线流量大优缓存一致性友好性能场景单核 / 低竞争尚可多核高竞争拉胯多核 / 高竞争首选现代标准实现灵活性仅能做简单自旋锁可实现无锁队列、公平锁、futex 等
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2550912.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!