一、读者写者问题定义
存在一个多个进程共享的数据区,该数据区可以是一个文件或一块内存空间,甚至可以是一组寄存器:有些进程(reader)只读取这个数据区中的数据,有些进程(writer)只往数据区中写数据。
此外,还必须满足以下条件
1、任意数量的读进程可同时读这个文件。
2、一次只能有一个写进程可以写文件。
3、若存在一个写进程正在写文件,则禁止任何读进程读文件。
4、若存在读进程正在读文件,则任何写进程需等待,直至当前的读进程全部执行读操作完毕!
也就是说,读进程不需要排斥其他读进程,而写进程需要排斥其他所有进程。
二、读者优先(C++代码)
#include <iostream>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>//sleep函数
using namespace std;
//信号量的数据类型为结构sem_t,它本质上是一个长整型的数
sem_t RW, Mutex;//信号量
int readCounts = 0;
typedef struct DATA{
int id;
int set_upTime;//启动时间
int exeTime;//执行时间
}data;
/* 测试例子
1 R 2 6
2 W 3 5
3 R 6 3
4 R 4 6
5 W 3 6
*/
//读者
void* Reader(void* tem)
{
int id = ((data*)tem)->id;
int set_upTime = ((struct DATA*)tem)->set_upTime;
int exeTime = ((data*)tem)->exeTime;
sleep(set_upTime);
printf("线程 %d: 等待“读”\n", id);
//用于确保readCounts被正确更新
sem_wait(&Mutex);//P//用来阻塞当前线程直到信号量mutex的值大于0,
//解除阻塞后将mutex的值减一,表明公共资源经使用后减少。
readCounts++;
if(readCounts == 1)
sem_wait(&RW);//等待读写权,且有优先使用权(较于写者)情况是当队列中有读者写者
//且读者排在写者前面,那么后来的读者可以排在最后一个等待读的后面/第一个等待写的前面
sem_post(&Mutex);//V
printf("线程 %d: 开始“读”\n", id);
sleep(exeTime);
printf("线程 %d: “读”结束\n", id);
sem_wait(&Mutex);
readCounts--;
if(readCounts == 0)
sem_post(&RW);//解锁
sem_post(&Mutex);
pthread_exit(0);
}
//写者
void* writer(void* tem)
{
int id = ((data*)tem)->id;
int exeTime = ((DATA*)tem)->exeTime;
int set_upTime = ((struct DATA*)tem)->set_upTime;
sleep(set_upTime);
printf("线程 %d: 等待“写”\n", id);
sem_wait(&RW);//P 等待读写权
printf("线程 %d: 开始“写”\n", id);
sleep(exeTime);
printf("线程 %d: “写”结束\n", id);
sem_post(&RW);//释放读写权
pthread_exit(0);
}
int main() {
int id = 0;
pthread_t tid; //用于声明线程ID
pthread_attr_t attr; //线程 属性
//pthread_attr_init(&attr);//初始化一个线程对象
sem_init(&Mutex, 0, 1);//用来初始化一个信号量
//第一个参数: 信号量名
//第二个参数: 表示允许几个进程共享该信号量,0表示用于进程内的多线程共享
//第三个参数: 表示可用的资源的数目
sem_init(&RW, 0, 1);
cout<<"请依次输入以下四项信息"<<endl;
cout<<"线程ID"<<" | "<<"身份"<<" | "<<"启动时间"<<" | "<<"执行时间"<<endl<<endl;
while(scanf("%d", &id) != EOF)
{
char role; //读者"R" or 写者"W"
int set_upTime; //启动时间
int exeTime; //运行时间
scanf("%c%d%d", &role, &set_upTime, &exeTime);
//data* d = ( data*)malloc(sizeof( data));
data* d = new DATA;//本句和上一句效果是一样的
d->id = id;
d->set_upTime = set_upTime;
d->exeTime = exeTime;
if(role == 'R'||role == 'r') {
printf("创建线程 %d: 读者\n\n", id);//Reader Create the %d thread
pthread_create(&tid, &attr, Reader, d);//创建线程
//第一个参数为指向线程 标识符的 指针
//第二个参数用来设置线程属性
//第三个参数是线程运行函数的起始地址
//最后一个参数是运行函数的参数
}
else if(role == 'W'||role == 'w') {
printf("创建线程 %d: 写者\n\n", id);//Writer
pthread_create(&tid, &attr, writer, d);
}
//cout<<"Read/Write operations all execute over!"<<endl;
}
//信号量销毁
sem_destroy(&Mutex);
sem_destroy(&RW);
return 0;
}
三、代码说明
写进程比较简单,信号量RW用于实施互斥,只要一个写进程正在访问共享数据区时。其他写进程和读进程都不能访问它。
读进程也使用RW实施互斥,但为了允许多个读进程在没有读进程正在读时,第一个试图读的读进程需要在RW上等待。当至少已有一个读进程正在读时,随后的读进程无须等待,可以直接进入,全局变量readCounts用于记录读进程的数量,信号量Mutex用于确保readCounts被正确地更新。
此内容为操作系统书中知识,比较简单、易实现,适于学习、理解信号量原理。
转载需说明!