- 姓名:巫艳珍
- 学号:201821121034
- 班级:计算1812
1.读者-写者问题
(1)读者-写者问题描述如下情况:对象在多个线程之间共享,一些线程只读数据,一些线程只写数据。为保证写入和读取的正确性,操作限制:
- 写-写互斥,即不能有两个写者同时进行写操作。
- 读-写互斥,即不能同时有一个线程在读,而另一个线程在写。
- 读-读允许,即可以有一个或多个读者在读。
(2)解决方案:读者优先或写者优先。
2.伪代码
(1)读者优先
当已经有线程在读数据的时候,其他读线程无需等待,而写线程需要等待所有正在进行的读操作之后才能执行。
semaphore rw=1;//实现对文件的互斥访问,表示当前是否有进程访问共享文件 int readcount=0;//记录访问文件的读进程数 semaphore mutex=1;//保证互斥访问 //写者 writer(){ while(1){ P(rw);//写文件前进行加锁 写文件 V(rw);//写文件之后进行解锁 } } //读者 reader(){ while(1){ P(mutex);//读进程互斥访问readcount if(readcount==0) P(rw);//第一个读进程“加锁”,阻塞写者 readcount++;//访问文件的读进程数加1 V(mutex); 读文件 P(mutex); readcount--; if(readcount==0) V(rw);//解锁 V(mutex); } }
问题:只有所有读者都读完,写者才能写,当读者过多时,会导致写者出现“饿死”的情况
(2)写者优先
写者优先需要满足的要求:多个读者可以进行读;写者要互斥,只允许一个写者写,不能读者写者同时进行;写者优先于读者,一旦有写者,后续的读者必须等待,唤醒时优先考虑写者,直到最后的写者完成操作。
尽量满足写操作,不能并发,但可以排队,优先于等待的读线程获得执行权
int readcount=0,writecount=0; 5个信号量,mutex1,mutex2,mutex3,r,w writer(){//写进程 P(mutex2); writecount++; if(writecount==1) P(r);//第一个为写者,阻止后面的读者 V(mutex2); P(w); 写文件 V(w); P(mutex2); writecount--; if(writecount==0) V(r); V(mutex2); } reader(){//读者进程 P(mutex3); P(r); P(mutex1); readcount++; if(readcount==1)//第一个读者,互斥写者 P(w); V(mutex1); V(r) V(mutex3); 读文件 P(mutex1); readcount--; if(readcount==0) V(w);//读者为最后一个,唤醒写者 V(mutex1); }
3.完整代码
#include<string.h> #include<unistd.h> #include<pthread.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/types.h> #include<sys/stat.h> #include<sys/shm.h> #include<semaphore.h> #include<stdio.h> int readcount=0,writecount=0; pthread_t p1,p2; sem_t mutex1,mutex2,mutex3,r,w;//5个信号量 void* writer(void* arg) { int i = *(int*)arg; printf("进程%d想要写入\n",i); sem_wait(&mutex2); writecount++; if(writecount==1) //若第一个为写者,阻止后续的读者 sem_wait(&r); sem_post(&mutex2); sem_wait(&w);//写者互斥 printf("进程%d正在写入\n",i); sleep(4); printf("进程%d完成写入\n",i); sem_post(&w); sem_wait(&mutex2); writecount--; if(writecount==0) sem_post(&r);//所有的写者写完才能让P(r)的读者readcount增加 sem_post(&mutex2); } void* reader(void* arg){ int i = *(int*)arg; printf("进程%d想要读取\n",i); sem_wait(&mutex3); sem_wait(&r); sem_wait(&mutex1); readcount++; if(readcount==1)//若第一个为读者,互斥写者 sem_wait(&w); sem_post(&mutex1); sem_post(&r); sem_post(&mutex3); printf("进程%d正在读取\n",i); sleep(2); printf("进程%d完成读取\n",i); sem_wait(&mutex1); readcount--; if(readcount==0)//读者运行完释放写者,读写互斥 sem_post(&w); sem_post(&mutex1); } int main(int argc,char *argv[]){ int ret; int pro_num=atoi(argv[1]);//输入进程数 sem_init(&r,0,1);//初始化信号量 sem_init(&w,0,1); sem_init(&mutex1,0,1); sem_init(&mutex2,0,1); sem_init(&mutex3,0,1); readcount=0; writecount=0; if(argc!=2){ fprintf(stderr,"wrong\n"); return 1; } printf("共有%d个进程\n",pro_num); int n[pro_num+1]; for(int i=0;i<pro_num;i++){ int s=rand()%2+1; int f=rand()%2; sleep(s); n[i]=i; if(f){ ret=pthread_create(&p1,NULL,reader,&n[i]); if(ret!=0){ printf("error to create reader!\n"); exit(1); } } else{ ret=pthread_create(&p2,NULL,writer,&n[i]); if(ret!=0){ printf("error to create writer!\n"); exit(1); } } } pthread_join(p2,NULL); pthread_join(p1,NULL); sleep(20); return 0; }
- 信号量常用函数:
sem_init:初始化信号量sem_t,初始化的时候可以指定信号量的初始值,以及是否可以在多进程间共享。
sem_wait:一直阻塞等待直到信号量>0。
sem_post:使信号量加1。
- pthread_join()函数:以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。
4.结果截图及解释
(1)结果截图:
(2)解释:实现的是写者优先
一共十个进程,进程2等待进程1完成写入后进行读取,进程3想要读取,能够实现与进程2同时读取,进程4想要读取,进程2和3读取完毕进程4开始写入,此时进程5、6、7想要读取,只有等进程4完成写操作才能读取,当进程9和进程10均想要写入时,不能同时进行写操作,待进程9完成写操作时进程10才写入。
5.遇到的问题
在编译的时候出现报错collect2:error:Id returned 1 exit status
解决:编译时连接库,在编译命令后添加:-lpthread -ldl -lm