计算机单片机毕设实战-基于 STM32 的室内多维度空气质量感知与自动调控系统设计 基于 STM32 与 ESP01S 的物联网室内环境监测终端设计与实现(010105)
2026/8/27 13:10:07
核心定义:
自旋锁是一种非阻塞锁。当线程尝试获取锁失败时,它不会挂起(阻塞/让出 CPU),而是会在一个死循环中持续检查(忙等待 / Busy-Waiting)锁是否被释放。
直观隐喻:
您可能会问:“让线程空转浪费 CPU,这不是很傻吗?”
要理解它的价值,必须看**上下文切换(Context Switch)**的成本。
std::mutex拿不到锁时,线程会陷入内核态(Kernel Mode)。pNext = node;的链表操作),耗时可能只有0.01 微秒。结论:自旋锁适用于**“锁持有时间极短”**的场景。
C++ 标准库并没有直接提供std::spinlock(C++20 只有std::atomic_flag),我们需要利用原子操作自己实现。
std::atomic_flag这是 C++ 中唯一保证**无锁(Lock-Free)**的数据类型。
#include <atomic> #include <thread> #include <vector> #include <iostream> class SpinLock { private: // atomic_flag 只有两个状态:set (true) 和 clear (false) // ATOMIC_FLAG_INIT 初始化为 false std::atomic_flag flag = ATOMIC_FLAG_INIT; public: void lock() { // test_and_set(): // 1. 读取当前值 // 2. 将值设为 true // 3. 返回旧值 // 这是一个原子操作 (RMW: Read-Modify-Write) // 如果返回 true,说明之前已经是 true (被别人锁了),则一直循环 (自旋) // memory_order_acquire: 保证获得锁之后的读写操作不会重排到加锁之前 while (flag.test_and_set(std::memory_order_acquire)) { // 这里是自旋区 (Spinning) // 可以在这里加 "CPU pause" 指令优化(后面会讲) } } void unlock() { // 清除标志,设为 false // memory_order_release: 保证解锁之前的读写操作全部完成 flag.clear(std::memory_order_release); } }; // 使用示例(配合 lock_guard 满足 RAII) SpinLock sl; void worker() { // std::lock_guard 需要类满足 BasicLockable (有 lock/unlock 方法) std::lock_guard<SpinLock> guard(sl); // 临界区... }std::atomic<bool>功能类似,但atomic<bool>可以提供更多 API(比如load查看状态),只是在极老的硬件上可能不是 Lock-Free 的(虽然现在几乎都是)。
C++
class SpinLockBool { std::atomic<bool> locked{false}; public: void lock() { bool expected = false; // CAS (Compare And Swap) // 尝试把 locked 从 false 改成 true // 如果 locked 是 true (被锁),compare_exchange_weak 返回 false,继续循环 while (!locked.compare_exchange_weak(expected, true, std::memory_order_acquire)) { expected = false; // CAS 失败后 expected 会被改成当前值(true),重置为 false 再次尝试 } } void unlock() { locked.store(false, std::memory_order_release); } };在实现高性能组件(如内存池)时,直接用while(flag.test_and_set())会带来严重的性能问题。
CAS(写操作)。load(读) 检查是否被释放(读操作不独占 Cache Line)。false时,才尝试CAS(写)。while循环是一个极紧密的指令序列,CPU 流水线会全速运行,产生大量热量并消耗电力。在 x86 架构下,使用 _mm_pause() 指令(SSE2 扩展)。
优化后的 C++ 代码:
#include <atomic> #include <immintrin.h> // for _mm_pause class OptimizedSpinLock { std::atomic_flag flag = ATOMIC_FLAG_INIT; public: void lock() { while (flag.test_and_set(std::memory_order_acquire)) { // 在自旋期间... while (flag.test(std::memory_order_relaxed)) { // 先只读 (Test) // 告诉 CPU 稍微休息一下,不要全速空转 #if defined(__x86_64__) || defined(_M_X64) _mm_pause(); #endif // 如果是 ARM 架构,可以用 __yield() 或 asm("yield") } } } void unlock() { flag.clear(std::memory_order_release); } };std::this_thread::yield()主动让出时间片。