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

阅读本文大概需要 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、混合锁:互斥锁+自旋锁

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

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

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

你可能感兴趣的:(【并发编程】线程安全之锁)