自旋锁 spinlock

spinlock与mutex的区别

自旋锁和互斥锁都是用于保护共享资源的锁,但它们的实现方式和特点有所不同:

  1. 实现方式:互斥锁采用的是阻塞等待的方式,即当一个线程请求锁时,如果锁已经被其它线程占用了,那么该线程将被阻塞等待,直到该锁被释放。而自旋锁则采用循环等待的方式,即当一个线程请求自旋锁时,如果锁已经被其它线程占用,则该线程会循环等待,直到轮到它获取锁为止。

  2. 线程调度机制:互斥锁阻塞等待时,会把线程从运行队列中移除,等待解锁时再加入运行队列。而自旋锁循环等待时,线程不会被移除运行队列,而是等待占用锁的线程释放锁。

  3. 锁持有时间:由于自旋锁不会引起线程的上下文切换,所以自旋锁的开销更小,适用于保护非常短小的代码区段。而互斥锁的上下文切换开销较大,适用于保护较长的代码区段。

总之,自旋锁适用于保护非常短小的代码区段,而互斥锁适用于保护较长的代码区段。选择哪种锁要根据实际情况进行选择。

适用场景

  1. 线程持有锁的时间很短,在线程放弃 CPU 时间等待操作系统调度时,会浪费很多 CPU 资源。自旋锁可以避免线程进入内核态,以轻量级的方式等待锁的释放,减少了上下文切换的开销。

  2. 共享资源的访问冲突较少。当访问冲突概率很低时,使用互斥锁的代价有可能会超过自旋锁的代价,因为互斥锁可能会导致线程进入内核态,而这时出现的等待时间可能远远大于自旋锁的自旋锁定时间。

  3. 多处理器系统。在多处理器系统中,不同的线程可以在不同的 CPU 上执行,而自旋锁可以利用处理器的缓存,避免对内存的频繁访问,提高缓存命中率,从而提高性能。

  4. 自旋锁和阻塞锁不一样,线程申请不到时不会挂起,一直自旋,目的是为了节省上下文切换的开销,在用户层使用时,不涉及到内核参与。但是一个线程如果获取不到锁,就会一直处于自选当中,特别费CPU。 因此一般用在竞争不激烈,临界区小、快进快出的场合。特别是临界区内千万不要调用那些阻塞型的api,防止别的线程因得不到锁而让CPU长时间处于自旋中。

  5. 但是尽管临界区很小,也有可能遇到特殊场合,比如一个线程在临界区被调度了,收到信号后去执行信号handler了,或者访问内存时发生缺页异常,要分配物理页面,甚至要从交换分区加载页面,这些都导致自旋锁被长时间占用得不到释放,让别的CPU一直处于自选中。

  6. 因此,在实现自旋锁时一般要进行优化,让自选的成本尽可能低,比如使用本地自旋方案,防止出现锁内存总线,导致频繁的缓存刷新和内存总线竞争,再者,像x86这样的处理器,还提供了pause指令,在得不到自旋锁时,可以执行它,让CPU暂时停止,这样可以降低电量消耗,更重要的是,如果CPU特供了超线程机制,一个逻辑核pause之后,就可以把所有的执行资源让给另一个逻辑核独享,也能提高运行速度。

  7. 需要注意的是,自旋锁并不适用于任何场景。当线程持有锁的时间较久,等待线程的数量很多,或者共享资源的访问冲突概率很高时,使用自旋锁可能会导致饥饿现象和优先级反转等问题,这时应该使用互斥锁等其他锁机制。

代码示例

#include <atomic>
#include <thread>
#include <iostream>

class SpinLock {
public:
    SpinLock() : flag(ATOMIC_FLAG_INIT) {}

    void lock() {
        while (flag.test_and_set(std::memory_order_acquire)) {
            // 自旋等待
        }
    }

    void unlock() {
        flag.clear(std::memory_order_release);
    }

private:
    std::atomic_flag flag;
};


SpinLock lock;

void print_thread_id(int id) {
    lock.lock();
    std::cout << "Thread id: " << id << std::endl;
    lock.unlock();
}

int main() {
    std::thread t1(print_thread_id, 1);
    std::thread t2(print_thread_id, 2);
    t1.join();
    t2.join();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值