经典同步问题之读者写者问题
读者写者问题中,有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间,有一些只读取这个数据区的进程(读者)和一些只往数据区写数据的进程(写者)。此外还需要满足以下条件:
- 任意多个读者可以同时读这个文件
- 一次只能有一个写者可以往文件中写(写者必须互斥)
- 如果一个写者正在操作,禁止任何读进程读文件和其他任何写进程写文件
读者优先算法
一个读者试图进行读操作,如果这时正有其他读者进行读操作,他可以直接开始读,而不需要等待。
读者陆续到来,读者一到就能够开始读操作,而写者进程只能等待所有读者都退出才能够进行写操作
信号量:
- 记录读者数量的整型变量readcount,初值为0,当值大于0时,表明有读者存在,写者不能进行写操作
- 互斥信号量rmutex,初值为1,用户保证多个读者进程对于readcount的互斥访问
- 互斥信号量dmutex,初值为1,用于控制写者进程对于数据区的互斥访问。
semaphore rmutex = 1;
semaphore dmutex = 1;
int readcount = 1;
void reader(){
while(true){
P(rmutex);
if(++readcount == 1) P(dmutex);
V(rmutex);
//此处为读操作
P(rmutex);
if(--readcount == 0) V(dmutex);
V(rmutex);
}
}
void writer(){
while(true){
P(dmutex);
// 此处为写操作
V(dmutex);
}
}
Linux中利用pthread库测试代码:
#include <bits/stdc++.h>
#include <time.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
using namespace std;
//semaphores
sem_t rmutex, dmutex;
int readcount;
struct Data{
int id;
int startTime, lastTime;
Data(){}
Data(int id, int sT, int lT):id(id), startTime(sT), lastTime(lT){}
};
void *Reader(void *d){
Data* t = (Data*) d;
int id = t->id;
int startTime = t->startTime;
int lastTime = t->lastTime;
sleep(startTime);
sem_wait(&rmutex);
printf("Waiting: %d Reader
", id);
readcount ++;
if(readcount == 1) sem_wait(&dmutex);
sem_post(&rmutex);
printf("Reading: %d Reader
", id);
sleep(lastTime);
printf("Finished: %d Reader
", id);
sem_wait(&rmutex);
readcount --;
if(readcount == 0) sem_post(&dmutex);
sem_post(&rmutex);
pthread_exit(0);
}
void *Writer(void *d){
Data* t = (Data*) d;
int id = t->id;
int startTime = t->startTime;
int lastTime = t->lastTime;
sleep(startTime);
printf("Waiting: %d Writer
", id);
sem_wait(&dmutex);
printf("Writing: %d Reader
", id);
sleep(lastTime);
printf("Finished: %d Writer
", id);
sem_post(&dmutex);
pthread_exit(0);
}
int main(){
pthread_t tid;
sem_init(&rmutex, 0, 1);
sem_init(&dmutex, 0, 1);
readcount = 0;
int id, startTime, lastTime;
string role;
while(~scanf("%d", &id)){
cin >> role >> startTime >> lastTime;
Data *d = new Data(id, startTime, lastTime);
if(role == "R"){
printf("Created: %d reader
", id);
pthread_create(&tid, NULL, Reader, d);
} else if(role == "W"){
printf("Created: %d Writer
", id);
pthread_create(&tid, NULL, Writer, d);
} else puts("Invalid Inputs");
}
pthread_exit(0);
}
/*
1 R 1 3
2 W 2 4
3 R 3 5
4 R 4 6
5 W 5 7
*/
编译命令: g++ readfirst.cpp -o readfirst -lpthread
运行:./readfirst
2号写者在2时刻申请写,但实际情况时,直到4号读者读完,2号才开始写
写者优先算法
当写者和读者同时等待时,后续写者到达可以插队到等待的读者之前,只要等待队列中有写者,不管何时到达,都优先于读者被唤醒。
相对于读者优先,新增的信号量:
- 互斥信号量readable,初值为1,用于控制写者到达时可以优先读者进入临界区
- 整型信号量 writecount,初值为0,表示当前写者的数量
- 互斥信号量wmutex,初值为1,用户控制写者互斥访问writecount
semaphore dmutex = 1;
semaphore rmutex = 1;
semaphore wmutex = 1;
semaphore readable = 1;
int readcount = 0, writecount = 0;
void reader(){
P(readable); //检查是否有写者,若没有则占用,进行后续操作
P(rmutex); //占用rmutex,准备修改readcount
if(++readcount == 1) P(dmutex); //若是第一个读者,占用数据区
V(rmutex); //释放rmutex
V(readable); //释放readable
// 此处为读操作
P(rmutex); //占用rmutex
if(--readcount == 0) V(dmutex); //若为最后一个读者,释放数据区
V(rmutex); //释放rmutex
}
void writer(){
P(wmutex); //占用wmutex,准备修改writecount
if(++writecount == 1) P(readable); //若为第一个写者,则占用readable,阻止后续读者进入
V(wmutex); //释放wmutex
P(dmutex); //占用dmutex,占用数据区
// 此处为写操作
V(dmutex); //释放rmutex
P(wmutex); //占用wmutex,修改writecount
if(--writecount == 0) P(readable); //若是最后一个写者,则释放readable允许后续读者进入
V(wmutex); //释放wmutex
}
实测代码:
#include <bits/stdc++.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
using namespace std;
sem_t dmutex, rmutex, wmutex, readable;
int readcount, writecount;
struct Data{
int id;
int startTime, lastTime;
Data(int id=0,int startTime=0, int lastTime = 0){
this->id = id;
this->startTime = startTime;
this->lastTime = lastTime;
}
};
void *Reader(void *d){
Data* t = (Data*) d;
int id = t->id;
int startTime = t->startTime;
int lastTime = t->lastTime;
sleep(startTime);
printf("Wating: %d Reader
", id);
sem_wait(&readable);
sem_wait(&rmutex);
readcount ++;
if(readcount == 1) sem_wait(&dmutex);
sem_post(&rmutex);
sem_post(&readable);
printf("Reading: %d Reader
", id);
sleep(lastTime);
printf("Finished: %d Reader
", id);
sem_wait(&rmutex);
if(--readcount == 0) sem_post(&dmutex);
sem_post(&rmutex);
pthread_exit(0);
}
void *Writer(void *d){
Data* t = (Data*) d;
int id = t->id;
int startTime = t->startTime;
int lastTime = t->lastTime;
sleep(startTime);
printf("Wating: %d Writer
", id);
sem_wait(&wmutex);
if(++writecount == 1) sem_wait(&readable);
sem_post(&wmutex);
sem_wait(&dmutex);
printf("Writing: %d Writer
", id);
sleep(lastTime);
printf("Finished: %d Writer
", id);
sem_post(&dmutex);
sem_wait(&wmutex);
if(--writecount == 0) sem_post(&readable);
sem_post(&wmutex);
pthread_exit(0);
}
int main(){
freopen("process", "r", stdin);
pthread_t tid;
sem_init(&rmutex, 0, 1);
sem_init(&dmutex, 0, 1);
sem_init(&wmutex, 0, 1);
sem_init(&readable, 0, 1);
readcount = writecount = 0;
int id, startTime, lastTime;
string role;
while(~scanf("%d", &id)){
cin >> role >> startTime >> lastTime;
Data *d = new Data(id, startTime, lastTime);
if(role == "R"){
printf("Created: %d reader
", id);
pthread_create(&tid, NULL, Reader, d);
} else if(role == "W"){
printf("Created: %d Writer
", id);
pthread_create(&tid, NULL, Writer, d);
} else puts("Invalid Inputs");
}
pthread_exit(0);
}
/*
1 R 1 10
2 R 2 1
3 W 3 10
4 R 14 3
5 W 15 5
*/
3号写者正在写时,4号读者先于5号读者到来,而当3号写者完成写操作时,5号写者率先一步开始写操作