【并发编程】线程安全之锁

本文深入解析了互斥锁、自旋锁及混合锁的工作原理与优缺点,互斥锁能最大化CPU利用率,但线程切换成本高;自旋锁减少上下文切换,适合轻量级同步,但可能造成CPU空转;混合锁结合两者优势,提高并发效率。

阅读本文大概需要 2 分钟。

1、互斥锁(mutex)

同一时刻只能有一个线程获得互斥锁,其余线程处于挂起状态

优点:cpu利用最大化。它发现资源被锁住,请求就排队等候。线程切换到别处干活,直到接受到可用信号,线程再切回来继续处理请求

缺点:获取不到锁的时候会进入阻塞状态,从而进入内核态;当获取到锁的时候会从内核态切换到用户态,需要线程上下文切换; 线程的休眠和唤醒都是相当昂贵的操作,它们需要大量的CPU指令

加锁

void mutex_lock() {
    lock(bus);    //给总线加锁,防止其他核的线程访问到lock
    if (mutex == 0) {
        mutex = 1
        return
    } else {
         block() // 调用系统函数将当前线程阻塞,进入内核态(内核调用)
         goto mutex_lock
    }
    unlock(bus);  
}

释放锁

void mutex_unlock() {
    lock(bus);    //给总线加锁,防止其他核的线程访问到lock
    mutex = 0
    wakeup() // 唤醒等待Mutex的线程(内核调用)
    unlock(bus)
}

2、自旋锁

当某个线程获得自旋锁后,别的线程会一直做循环,尝试加锁

优点:自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快

缺点:1.长时间获取不到锁的话,cpu空转,耗cpu; 2. 不适用单核/单CPU的系统上,无法解锁

加锁

void spin_lock() {
    // cas原子操作 CMPXCHG (cas是硬件支持的原子操作,不需要进入内核态)
    while(!cas(state, 0, 1)) {
    
    }
    持有者 = 当前线程
}

释放锁

void spin_unlock() {
    if(持有者 == 当前线程) {
        持有者 = null
        state = 1
    } else {
        throw Exception
    }
}

3、混合锁:互斥锁+自旋锁

混合锁是先自旋锁一段时间或自旋多少次,再转成互斥锁

优点:互斥锁和自旋锁的折中方案,利用前二者优点,避免出现极端情况(自旋时间过长,互斥锁切换过多)

缺点:自旋多少时间、自旋多少次,这些策略很难把控。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值