第3关:项目实战
任务描述
本关任务:利用信号量实现一个读写锁。
相关知识
Linux 系统中提供了现成的读写锁库函数,也就是上一关我们学习的。其实,我们利用其他线程同步的方法也可以实现一个读写锁。
读写锁 需要满足如下规则:
- 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁;
- 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁;
通过实训"Linux之线程同步一"的学习,我们现在知道如何互斥锁和条件变量来同步线程。那么利用 互斥锁 和 条件变量 知识就可以简单的读写锁。
利用条件变量和互斥锁实现读写锁
使用条件变量和互斥锁实现读写锁,根据读写锁的特性,当有读者在读取数据时,则不能有线程对数据进行写操作,并且同时可以存在多个读者。当有写者在对数据进行写操作的时候,则不能有线程对数据进行读操作,并且同一时刻只能有一个写者。因此,实现一个简单的读写锁可以分为以下几步:
1. 定义两个变量用于记录读者(readNum)和写者(writeNum)的个数;
2. 对于写模式的加锁,如果readNum和writeNum同时为0,则将writeNum设置为1表示此时有一个写者需要对数据进行写操作;否则,写模式的加锁操作处于等待状态;
3. 对于写模式的解锁,如果完成的写操作,此时需要将writeNum设置为0,表示此时没有写操作,并且通知读者可以读取数据了;
4. 对于读模式的加锁,如果writeNum为0,则表示当前没有写操作,可以读取数据,并且将readNum值加一表示多了一个读者;否则,读模式的加锁操作处于等待状态;
5. 对于读模式的解锁,如果完成的读操作,此时需要将readNum减一操作,然后判断readNum是否为0,如果为零,则通知写者可以执行写操作;
利用互斥锁实现读写锁
只使用互斥锁也可以实现读写锁,详细的步骤可分为以下几步:
1. 定义一个变量用于记录读者(readNum)的个数和两个互斥锁,分别是读模式的互斥锁(mutex_read)和写模式的互斥锁(mutex_write);
2. 对于写模式的加锁,直接对mutex_write进行加锁操作即可;
3. 对于写模式的解锁,直接对mutex_write进行解锁操作即可;
4. 对于读模式的加锁,首先判断读者的数量是否为0,如果为0,则表示第一个读者要去读取数据,那么此时要禁止写者进行写数据操作,所以对mutex_write进行加锁操作并设置readNum++;否则直接将readNum++即可;
5. 对于读模式的解锁,首先将readNum--,然后判断此时是否readNum为0,如果为0,则表示现在允许写者可以写数据,因此要对mutex_write进行解锁操作;
编程要求
本关的编程任务是补全右侧代码片段中Begin
至End
中间的代码,具体要求如下:
- 利用信号量实现读写锁功能;
- 补全
sem_rwlock_rdlock
和sem_rwlock_unrdlock
函数; sem_rwlock_rdlock
函数用于读模式下的读加锁操作;sem_rwlock_unrdlock
函数用于读模式下的读解锁操作;- 提示:参考两个互斥锁和一个变量实现读写锁的方式,互斥锁其实就是 0-1 信号量;
- 评测读写锁实现是否正确所使用的测试用例与上一关测试用例一致,详细描述参考上一关编程要求介绍;
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
//记录读线程的个数
extern int reader;
//全局的信号量变量
extern sem_t sem_read, sem_write;
//读写锁初始化函数
void sem_rwlock_init()
{
reader = 0;
//初始化信号量个1
sem_init(&sem_read, 0, 1);
sem_init(&sem_write, 0, 1);
}
//读写锁注销函数
void sem_rwlock_destroy()
{
sem_destroy(&sem_read);
sem_destroy(&sem_write);
}
//读模式下的加锁操作
void sem_rwlock_rdlock()
{
//读模式下加锁操作
/********** BEGIN **********/
sem_wait(&sem_read);
if(reader == 0)
sem_wait(&sem_write);
reader++;
sem_post(&sem_read);
/********** END **********/
}
//读模式下的解锁操作
void sem_rwlock_unrdlock()
{
//读模式下解锁操作
/********** BEGIN **********/
sem_wait(&sem_read);
reader--;
if(reader == 0)
sem_post(&sem_write);
sem_post(&sem_read);
/********** END **********/
}
//写模式下的加锁操作
void sem_rwlock_wrlock()
{
sem_wait(&sem_write);
}
//写模式下的解锁操作
void sem_rwlock_unwrlock()
{
sem_post(&sem_write);
}