并发中的锁机制

发布于 2021-09-13  41 次阅读


Linux下的并发控制

互斥锁

这个算是最常见的锁,一个互斥量。只有获取到互斥锁之后才能继续运行,具有排他性。

从互斥锁中可以衍生出条件变量,条件变量可以看作一个在互斥锁保护下的等待队列。线程上锁之后检查条件,如果不满足则释放锁,并把自己添加到等待队列中。之后等待其他线程唤醒(其他线程在发送唤醒信号前需要解锁,否则被唤醒线程无法获得锁就直接陷入死锁)。

此外还有一种递归互斥锁,即可以在一个线程中重复加锁,线程让出资源的时候也需要进行同样次数的解锁。

读写锁

这种锁适用于读取比写入频繁的场景,一个共享的读取锁,可以让多个读线程同时读取。一个互斥的写锁,同时只有一个线程可以写入。

这种情况下会出现一个写线程饥饿的场景,即读取线程一直占用着读写锁,导致写线程无法写入。这种情况可以采用写优先策略。即设置一个变量,可以是一个队列或者标志位。当读线程发现写入队列不为空,或标志位被设置之后。主动让出资源,让写线程优先写入。

自旋锁

特点:轮询忙等待。
在单核cpu下不起作用:被自旋锁保护的临界区代码执行时不能进行挂起状态。会造成死锁
自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。

信号量

与互斥锁类似,不同的是互斥锁是单值的,只能表示一个资源。而信号量可以表示多个资源。

乐观锁

前面提到的诸多锁都属于悲观锁,即认为一定会有其他线程同时对临界区进行修改,所以进行加锁保护。

而乐观锁并不强制上锁保护,而是在出现冲突的时候提示错误信息,并进行下一步处理。例如数据库的条目中带有version字段。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。


当其他人都认为你要鸽的时候,你鸽了,亦是一种不鸽