自旋锁
自旋锁的概念和理解
锁在处理需要申请加锁的线程的时候,一般有两种处理方法:一种是挂起等待,另外一种是自旋。自旋即轮询。
挂起等待:
当一个线程成功申请锁,并进入临界区后,其它线程在申请的时候会被挂起等待,即处于阻塞状态。
自旋锁:
当一个线程成功申请锁,并进入临界区后,其它线程在申请的时候会不断地来查看这个线程是否释放了锁,即不断轮询,如果释放了,就会竞争锁。
那么是什么决定了使用哪种处理方式呢?
决定于在临界区中的线程要在临界区呆多长时间。但是是没有定义时间长短的。
一般来说,我们都采用挂起等待的方式,而不会采用自旋,因为自旋会很消耗CPU资源,挂起等待不会占用CPU资源。除非我们能够确定当前情况非常适合自旋。
自旋锁的接口介绍:
加锁:
解锁:
自旋锁的初始化:
我们能够发现,自旋锁跟我们使用一般的锁的接口很像,比如
读者写者问题
读写锁概念
在多线程的场景下,有一种情况很常见,那就是公共数据很少会去被修改,即很多情况都是只读,然后很少写。如果在这种大多只读的情况下, 我们还要对线程进行加锁解锁等等,会有很多不必要的消耗。因此,读写锁就能够专门处理这种少写多读的情况。
读者写者跟生产消费者模型很像,其中,写者与读者的关系为互斥与 同步,写者与写者的关系为互斥,而读者与读者之间没有互斥和同步的关系。因为读者写者模型,读者不会拿走临界区的资源,因此也就没有读者与读者之间的互斥关系。
读写锁适合的场景是一次写入,大部分时间都在只读并且不做修改。
读写锁的接口了解:
初始化
写者的加锁
读者的加锁
解锁
这里我们可以观察到,锁的接口的使用方法很多都是一样的,因此学习成本也比较低,只要学会了mutex锁的接口使用方法就OK了。
读写锁的原理
接下来通过伪代码来了解一下读写锁的工作原理。
读者优先
当读者和写者竞争时,读者优先,当读者的数量大于0,那么就把写者的锁拿走,不让写者进入临界区。当读者的数量为0,那么写者申请锁,可以进入。
读者的加锁:
添加锁:
pthread_mutex_t rdlock;//锁的变量
int reader_count = 0;//初始化,用于计数,读者的数量
lock(&rdlock);//为读者加锁,保证线程安全
reader_count++;//读者的数量加一
if(reader_count==1) lock(&wrlock);//如果读者的数量为1,把写者的锁拿走,不让写者进来写了
unlock(&rdlock);//读者解锁
//读取数据
//......
lock(&rdlock);//加锁,保证线程安全
reader_count--;
if(reader_count==0) unlock(&wrlock);//当读者的数量为0,把写者的锁还回去
unlock(&rdlock);
写者的加锁:
pthread_mutex_t wrlock;
lock(&rwlock);
//写入数据
//.....
unlock(&wrlock);
写者优先
写者优先很少用到。所谓的写者优先,是在临界区中读者的数量大于0,后面还有读者想要申请进入临界区,此时写者就会比他们优先。