Skip to content

ipc::spin_lock

mutouyun edited this page Dec 9, 2025 · 3 revisions

轻量级自旋锁,用于短时间的临界区保护。适用于竞争不激烈、持锁时间极短的场景。

namespace ipc {

class spin_lock {
public:
    void lock() noexcept;
    void unlock() noexcept;
};

} // namespace ipc

概述

ipc::spin_lock是一个基于原子操作的自旋锁实现,不使用系统调用。当锁被占用时,线程会在循环中不断尝试获取锁("自旋"),而不是进入睡眠状态。

特点

  • ✅ 无系统调用开销,极快的lock/unlock
  • ✅ 适合持锁时间极短(微秒级)的场景
  • ⚠️ CPU消耗高,不适合长时间持锁
  • ⚠️ 仅适用于进程内同步,不支持进程间同步

成员函数

成员函数 说明
lock 获取自旋锁
unlock 释放自旋锁

lock

void lock() noexcept;

获取自旋锁。如果锁已被占用,当前线程会自旋等待(busy-waiting)直到获得锁。

自旋策略

  • 前4次尝试:不做任何操作,直接重试
  • 4-16次:使用CPU pause指令,降低功耗
  • 16-32次:调用std::this_thread::yield()让出CPU
  • 32次以上:睡眠1毫秒

返回值:无

异常:不抛出异常(noexcept)

unlock

void unlock() noexcept;

释放自旋锁。

返回值:无

异常:不抛出异常(noexcept)

注意:只有持有锁的线程才能解锁。


使用示例

基本用法

#include "libipc/rw_lock.h"  // spin_lock定义在此文件中
#include <thread>
#include <vector>

ipc::spin_lock g_lock;
int g_counter = 0;

void increment() {
    for (int i = 0; i < 10000; ++i) {
        g_lock.lock();
        ++g_counter;
        g_lock.unlock();
    }
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 4; ++i) {
        threads.emplace_back(increment);
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "Counter: " << g_counter << std::endl;  // 应该是40000
    return 0;
}

RAII封装

// 创建一个RAII风格的锁守卫
class spin_lock_guard {
    ipc::spin_lock& lock_;
public:
    explicit spin_lock_guard(ipc::spin_lock& lock) : lock_(lock) {
        lock_.lock();
    }
    ~spin_lock_guard() {
        lock_.unlock();
    }
    // 禁止拷贝
    spin_lock_guard(const spin_lock_guard&) = delete;
    spin_lock_guard& operator=(const spin_lock_guard&) = delete;
};

// 使用RAII守卫
void safe_increment() {
    spin_lock_guard guard(g_lock);
    ++g_counter;  // 自动加锁和解锁
}

与std::atomic的比较

// 使用spin_lock保护
ipc::spin_lock lock;
int value = 0;

void update_with_lock() {
    lock.lock();
    value = value + 1;  // 可以执行复杂操作
    lock.unlock();
}

// 使用atomic(仅适合简单操作)
std::atomic<int> atomic_value{0};

void update_atomic() {
    atomic_value.fetch_add(1, std::memory_order_relaxed);
}

性能特性

适用场景

适合使用spin_lock的场景

  • 临界区非常小(几条指令)
  • 持锁时间极短(微秒级)
  • 竞争不激烈
  • 高性能要求,不能容忍系统调用开销

不适合使用spin_lock的场景

  • 临界区较大或持锁时间长
  • 竞争激烈(多个线程频繁等待)
  • 需要进程间同步(应使用ipc::sync::mutex
  • 临界区内有IO操作或系统调用

性能对比

在无竞争情况下的性能(Intel i5-4300U):

锁类型 Lock/Unlock时间
spin_lock ~20ns
std::mutex ~100ns
ipc::sync::mutex ~100ns

注意:在高竞争情况下,spin_lock可能比mutex更慢,因为它会浪费CPU循环。


实现细节

内部实现

class spin_lock {
    std::atomic<std::uint32_t> lc_ { 0 };
public:
    void lock() noexcept {
        for (unsigned k = 0;
             lc_.exchange(1, std::memory_order_acquire);
             yield(k)) ;
    }
    
    void unlock() noexcept {
        lc_.store(0, std::memory_order_release);  
    }
};

关键点

  • 使用std::atomic<uint32_t>作为锁标志
  • lock()使用exchange原子操作尝试将0设置为1
  • unlock()将标志设回0
  • 使用memory_order_acquirememory_order_release保证内存可见性
  • 自适应的yield策略减少CPU浪费

注意事项

  • 不可递归:同一线程不能对已持有的spin_lock再次加锁(会死锁)
  • 不可拷贝:spin_lock对象不能拷贝或移动
  • 进程内使用:仅用于同一进程内的线程同步
  • 公平性:不保证公平性,可能出现饥饿
  • 异常安全:建议使用RAII包装类
  • 持锁时间:保持持锁时间尽可能短
  • 避免嵌套:不要在持有spin_lock时获取其他锁

相关文档

  • ipc::rw_lock - 读写锁(也是自旋锁实现)
  • ipc::sync::mutex - 进程间互斥锁
  • rw_lock.h - 头文件文档

Clone this wiki locally