读者—写者问题
一、读者—写者问题
对于读者写者,我们在操作系统中进程线程一课就已学习,再结合网上的资料我大致理解为如下几点:
- 读者和写者互斥,这里的互斥是指读者和写者不能同时访问共享资源,比如读者进行读文件的操作我们叫做读进程,写者进行写文件的操作我们叫做写进程,这两个进程不能同时进行,即读进程进行时写进程禁止,写进程进行时读进程禁止
- 多线程的读者写者问题可以允许有多个写进程和读进程。可以允许多个读进程同时读一个资源,但是不允许多个写进程同时访问一个资源
二、算法分类
1.写者优先算法
写者优先算法就是在满足读者写者互斥的情况下,谁先执行衍生出来的算法,其要点如下:
(1)如果读者来:
如果没有读者、写者在访问资源,新读者可以读;
如果没有写者等待,但有其他读者正在读,新读者可以读;
如果有写者等待,但有其他读者正在读,新读者等待;
如果有写者写,新读者等待
(2)如果写者来:
如果没有读者在读,新写者可以写;
如果有读者在读,新写者等待;
如果有其他写者或等待,新写者等待
2.读者优先算法
(1)有新读者进入
如果没有读者、写者在访问资源,新读者可以读;
如果没有写者正在写,新读者可以读;
如果有写者写,新读者等待。
(2)有新写者进入
如果没有读者在读,新写者可以写;
如果有读者在读,新写者等待;
如果有其他写者在写或在等待,新写者等待
三、实验代码
1.写者优先算法代码
所谓写者优先,即:当有读者进程正在执行,写者进程发出申请,这时应该拒绝其他读者进程的请求,等待当前读者进程结束后立即执行写者进程,只有在无写者进程执行的情况下才能够允许读者进程再次运行。为此,增加一个信号量并且在上面的程序中 writer()和reader()函数中各增加一对PV操作,就可以得到写进程优先的解决程序。
在读者优先的基础上
- 增加信号量r,初值是1,用于禁止所有的读进程。
- 增加一个记数器,即整型变量writecount,记录写者数,初值是0(原count改为readcount)。 writecount为多个写者共享的变量,是临界资源。用互斥信号量wmutex控制, wmutex初值是1(原wmutex改为mutex1)。
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#define N 5
int readcount=0,writecount=0,a=5,b=2;
int r[N]={0,1,2,3,4};
int w[N]={0,1};
sem_t wmutex,rmutex,mutex1,num;
void delay()
{
int time = rand() % 10 + 1; //随机使程序睡眠0点几秒
usleep(time * 100000);
}
void Reader(void *arg)
{
int i=*(int *)arg;
while(a>0)
{
a--;
delay();//延迟
//进入共享文件前的准备
sem_wait(&num);//在无写者进程时进入
sem_wait(&rmutex);//与其他读者进程互斥的访问readcount
if(readcount==0)
sem_wait(&mutex1);//与写者进程互斥的访问共享文件
readcount++;
sem_post(&rmutex);
sem_post(&num);
//reader
printf("Reader%d is reading!\n",i);
printf("Reader%d reads end!\n",i);
//退出共享文件后的处理
sem_wait(&rmutex);
readcount--;
if(readcount==0)
sem_post(&mutex1);
sem_post(&rmutex);
}
}
void Writer(void *arg)
{
int i=*(int *)arg;
while(b>0)
{
b--;
delay();
//进入共享文件前的准备
sem_wait(&wmutex);//保证多个写者进程能够互斥使用writecount
writecount++;
if(writecount==1)
sem_wait(&num);//用于禁止读者进程
sem_post(&wmutex);
//writer
sem_wait(&mutex1);//与其他所有进程互斥的访问共享文件
printf("writer%d is writing!\n",i);
printf("writer%d writes end!\n",i);
sem_post(&mutex1);
//退出共享文件后的处理
sem_wait(&wmutex);
writecount--;
if(writecount==0)
sem_post(&num);
sem_post(&wmutex);
}
}
int main()
{
int i;
pthread_t writer[N],reader[N];
srand((unsigned int)time(NULL));
sem_init(&wmutex,0,1);//互斥锁初始化
sem_init(&rmutex,0,1);
sem_init(&mutex1,0,1);
sem_init(&num,0,1);
for(i=0;i<5;i++)//创建线程
{
pthread_create(&reader[i],NULL,(void *)Reader,&r[i]);
}
for(i=0;i<2;i++)//创建线程
{
pthread_create(&writer[i],NULL,(void *)Writer,&w[i]);
}
for(i=0;i<2;i++)//等待线程
{
pthread_join(writer[i],NULL);
}
for(i=0;i<5;i++)//等待线程
{
pthread_join(reader[i],NULL);
}
sem_destroy(&rmutex); //互斥锁的销毁
sem_destroy(&wmutex);
sem_destroy(&mutex1);
sem_destroy(&num);
return 0;
}
2.读者优先算法
为实现Reader和Writer进程之间在读或写时的互斥而设置了一个互斥信号量wmutex。再设置一个整型变量conut表示正在读的进程数目。
-
仅当count=0时,Reader进程才需要执行wait(wmutex)操作;同理,仅当Reader进程在执行了count减一操作后其值为0时,才需要执行signal(wmutex)操作,以便让Writer进程操作;
-
由于count是一个可被多个Reader进程访问的临界资源,因此为其设置一个互斥信号量rmutex;
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#define N 5
int count=0,a=5,b=5;
int r[N]={0,1,2,3,4};
sem_t wmutex,rmutex;
void delay()
{
int time = rand() % 10 + 1; //随机使程序睡眠0点几秒
usleep(time * 100000);
}
void Reader(void *arg)
{
int i=*(int *)arg;
while(a>0)
{
a--;
delay();
sem_wait(&rmutex);
if(count==0)
sem_wait(&wmutex);
count++;
sem_post(&rmutex);
printf("Reader%d is reading!\n",i);
printf("Reader%d reads end!\n",i);
sem_wait(&rmutex);
count--;
if(count==0)
sem_post(&wmutex);
sem_post(&rmutex);
}
}
void Writer()
{
while(b>0)
{
b--;
delay();
sem_wait(&wmutex);
printf("writer is writing!\n");
printf("writer writes end!\n");
sem_post(&wmutex);
}
}
int main()
{
int i;
pthread_t writer,reader[N];
srand((unsigned int)time(NULL));
sem_init(&wmutex,0,1);//互斥锁初始化
sem_init(&rmutex,0,1);
for(i=0;i<5;i++)//创建线程
{
pthread_create(&reader[i],NULL,(void *)Reader,&r[i]);
}
pthread_create(&writer,NULL,(void *)Writer,NULL);
pthread_join(writer,NULL);//线程等待
sem_destroy(&rmutex); //互斥锁的销毁
sem_destroy(&wmutex);
return 0;
}
3.读写公平
为实现读写公平,我们必须要同时满足以下四个条件:
- 读者写者的优先级相同
- 读者、写者互斥访问
- 只允许有一个写者访问临界区
- 可以有多个读者同时访问临界区的资源
为此,我们设置一个互斥信号量queue,其作用是让Writer进程和Reader进程进行排队,当有Reader进程执行时,queue=0,因此Writer进程会进入等待,直到queue变为1;当Writer进程执行时,同理;因此我们借助queue实现了读写进程的优先级平等。其余的和读者优选算法中相同。
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#define N 5
int readcount=0,a=5,b=5;
int r[N]={0,1,2,3,4};
sem_t wmutex,rmutex,queue;
void delay()
{
int time = rand() % 10 + 1; //随机使程序睡眠0点几秒
usleep(time * 100000);
}
void Reader(void *arg)
{
int i=*(int *)arg;
while(a>0)
{
a--;
delay();
sem_wait(&queue); //让写者进程排队,读写进程具有相同的优先级
sem_wait(&rmutex); //与其他读者进程互斥的访问readcount
if(readcount==0) //最开始的时候readcount=0
sem_wait(&wmutex); //与写者进程互斥的访问共享文件
readcount++;
sem_post(&rmutex);
sem_post(&queue); //使得写者进程进入准备状态
//Reader
printf("Reader%d is reading!\n",i);
printf("Reader%d reads end!\n",i);
sem_wait(&rmutex);
readcount--;
if(readcount==0)
sem_post(&wmutex);
sem_post(&rmutex);
}
}
void Writer()
{
while(b>0)
{
b--;
delay();
sem_wait(&queue);
sem_wait(&wmutex);
printf("writer is writing!\n");
printf("writer writes end!\n");
sem_post(&wmutex);
sem_post(&queue);
}
}
int main()
{
int i;
pthread_t writer,reader[N];
srand((unsigned int)time(NULL));
sem_init(&wmutex,0,1);//互斥锁初始化
sem_init(&rmutex,0,1);
sem_init(&queue,0,1);
for(i=0;i<5;i++)//创建线程
{
pthread_create(&reader[i],NULL,(void *)Reader,&r[i]);
}
pthread_create(&writer,NULL,(void *)Writer,NULL);
pthread_join(writer,NULL);//线程等待
sem_destroy(&rmutex); //互斥锁的销毁
sem_destroy(&wmutex);
sem_destroy(&queue);
return 0;
}
四、实验截图
1.写者优先算法
2.读者优先算法
3.读写公平
本文参考资料:
(35条消息) 经典的进程同步问题-----读者-写者问题详解_李子树_的博客-CSDN博客
https://blog.csdn.net/aimat2020/article/details/121692122