读写锁的使用(读写者模型)

读写锁是互斥锁的一种,两者的区别在于:

  • 互斥锁:线程之间相互排斥,每次只能有一个线程访问临界资源
  • 读写锁
    • 写者(线程)相互排斥,每次只能有一个线程访问临界资源
    • 读者(线程)互不影响,可以同时访问临界资源。

在读取比修改频繁的场景下,读写锁要更合适一点。因为如果使用互斥锁,在读取数据的时候,读线程之间会争抢锁,但是使用读写锁时,读线程之间互不影响。

画黑板报就是一个典型的读写者模型,画板报的人只有一个(即写者),但是读黑板报的人有多个(即读者)


目录

一、模型中的三种关系

1、写者和写者

2、读者和写者

3、读者和读者

二、读写锁加锁/解锁的基本原理

1、读者加锁/解锁

2、写者加锁/解锁

三、读写锁的相关函数

1、初始化读写锁:pthread_rwlock_init

2、申请读者锁:pthread_rwlock_rdlock 

3、申请写者锁:pthread_rwlock_wrlock

4、解锁:pthread_rwlock_unlock

5、销毁读写锁:pthread_rwlock_destroy

四、读写锁的简单使用

五、注意事项


一、模型中的三种关系

两个角色指的是读者角色和写者角色,三种关系指的是读者和读者、写者和写者、读者和写者。

1、写者和写者

每次画黑板报的都只能有一个,两个人同时画的话,一个人画消防主题,另一个人却在画动物主题,这并不是我们想看到的。

==》写者和写者之间是互斥关系。必须要等一个人画完,才能让下一个画

2、读者和写者

有的时候,你可能认为,画黑板报和读黑板报是可以同时进行的,但是画的过程中,读的人可能无法get到你在画什么,明明你在画龙,读的人却说你在画蛇。这就引起误解了,所以应该等写者写完,读者再读。

另一个角度就是,读者还在读,写者却想把板报给擦了,很显然这就矛盾了,所以我们应该让读者读完,写者再写。

==》读者和写者之间是互斥关系。写者在写的时候,读者不能得到锁;读者在读的时候,写者也不能得到锁。

3、读者和读者

一个人读不会影响另一个人读,这里就不存在什么互斥关系了。

==》读者和读者不会互相影响

 

二、读写锁加锁/解锁的基本原理

读写锁使用读者的数目作为临界资源,即 int readers = 0;

1、读者加锁/解锁

因为读者和读者之间是没有关系的,所以加锁和解锁是两个独立的过程

加锁 = 读黑板报的人加1

解锁 = 读黑板报的人减1

// 加锁
lock();
readers++;
unlock();

// 解锁
lock();
readers--;
unlock();

2、写者加锁/解锁

写者申请锁的时候,就要看读者数量了,主要有两种情况

  • readers != 0:当有读者在的时候,写者就无法申请到锁,此时会进入条件变量等待
  • readers == 0:只有当读者为0时,此时写者会被唤醒去争抢锁
// 等待读者线程解锁
while(readers > 0)
{
    cond_wait();        // 进入条件变量等待(等到读者数目为0,发送信号将挂起的写者唤醒)
}

lock();          //加锁

modify();        //用来表示写者修改内容的操作

unlock();        //解锁

三、读写锁的相关函数

 从上面可以了解到,读锁和写锁本质上维护的是同一份临界资源。读者加锁的时候,读者数目加1;写者加锁的时候,必须要等到没有其他读者以后,才会去争抢锁。pthread库提供了 pthread_rwlock_t 这种结构体类型来表示读写锁。其他读写锁相关函数大致如下:

  • pthread_rwlock_init:初始化一个读写锁
  • pthread_rwlock_rdlock:读者申请锁
  • pthread_rwlock_tryrdlock:非阻塞读锁定  
  • pthread_rwlock_wrlock:写者申请锁
  • pthread_rwlock_trywrlock:非阻塞写锁定
  • pthread_rwlock_unlock:解锁读写锁
  • pthread_rwlock_destroy:释放读写锁

1、初始化读写锁:pthread_rwlock_init

pthread_rwlock_init 的作用是初始化一个读写锁。

第一个参数rwlock:你要初始化的读写锁。

第二个参数 attr:给读写锁设置属性。设为NULL的话表示使用默认属性。

返回值:成功返回0;失败返回一个错误码

2、申请读者锁:pthread_rwlock_rdlock 

pthread_rwlock_rdlock 的作用是申请读者锁,此时读者的数量会加1。

3、申请写者锁:pthread_rwlock_wrlock

pthread_rwlock_wrlock 的作用是申请写者锁,允许申请的条件是,读者的数量为0

4、解锁:pthread_rwlock_unlock

pthread_rwlock_unlock 的作用是为读者 / 写者锁解锁。因为读写锁保护的是同一个临界资源,解锁其实就是让这份临界资源可以被读者 / 写者访问到。

 

5、销毁读写锁:pthread_rwlock_destroy

pthread_rwlock_destroy 的作用是销毁读写锁。

参数rwlock:你要销毁的读写锁

返回值:成功返回0;失败返回一个错误码

 

四、读写锁的简单使用

现在有一个读线程,一个写线程,读线程在读数据的时候,写线程无法申请锁;写线程只能趁着读线程休眠的空挡来申请锁。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

pthread_rwlock_t rwlock;

void* read_thread(void* args){
	 pthread_detach(pthread_self());

 	 while (1)
 	 {
		 pthread_rwlock_rdlock(&rwlock);
		 printf("读者[1]正在读内容\n");
		 pthread_rwlock_unlock(&rwlock);   
         sleep(1);
 	 }    
}

void* write_thread(void* args){
	pthread_detach(pthread_self());

	while (1)
	{
		pthread_rwlock_wrlock(&rwlock);
		printf("写者[1]正在写内容\n");
		pthread_rwlock_unlock(&rwlock);        
        // sleep(1);
	}
}

int main(){
	pthread_t tid1,tid2,tid3,tid4;
	pthread_rwlock_init(&rwlock, NULL);
	pthread_create(&tid1,NULL,read_thread, NULL);
	pthread_create(&tid3,NULL,write_thread, NULL);
	while(1){    
		sleep(1);
	} 
	return 0;
}

 

五、注意事项

同一时刻,只有一个线程可以获得写锁,但是可以有多个线程获得读锁。

  • 读写锁处于写锁状态时,所有要申请读写锁的线程都会被阻塞。
  • 读写锁处于读锁状态时,写锁申请会阻塞等待,读锁不会受到影响
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值