作业0 解决管道读者写者饥饿问题
问题描述
写者:可以读,也可以写;读者:只读数据,不可写;多个读者可以同时读;写者修改数据时,其他写者和读者不可访问数据。
即:
先进入 | 后进入 | 是否可执行 |
---|---|---|
R | R | √ |
R | W | × |
W | R | × |
W | W | × |
最终尝试
所用到信号量
typedef struct{
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
}sema_t;
int readercount=0; // 当前读者数量
sema_t rw_sema; // 读写共用信号量
pthread_mutex_t rmutex; // 读操作上锁互斥信号量
写者线程函数
void *writer(void *arg){
sema_wait(&rw_sema);
printf("writing\n");
sleep(1); // 假设2s执行完写操作
sema_signal(&rw_sema);
}
读者线程函数
void *reader(void *arg){
pthread_mutex_lock(&rmutex);
if(readercount == 0)
sema_wait(&rw_sema);
readercount++;
pthread_mutex_unlock(&rmutex);
printf("reading\n");
sleep(1); // 假设2s执行完读操作
pthread_mutex_lock(&rmutex);
readercount--;
if(readercount == 0)
sema_signal(&rw_sema);
pthread_mutex_unlock(&rmutex);
}
测试用例
order[] = "RRWRWRRRRW"; // 表示读写者进入顺序 R:reader,W:writer
程序代码
#include<stdio.h>
#include<stdbool.h>
#include<pthread.h>
#include<unistd.h>
#define SIZE 100
typedef struct{
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
}sema_t;
int readercount=0;
sema_t rw_sema;
pthread_mutex_t rmutex;
void sema_init(sema_t *sema, int value){
sema->value = value;
pthread_cond_init(&sema->cond, NULL);
pthread_mutex_init(&sema->mutex, NULL);
}
void sema_wait(sema_t *sema){
pthread_mutex_lock(&sema->mutex);
if(sema->value <= 0)
pthread_cond_wait(&sema->cond, &sema->mutex);
sema->value--;
pthread_mutex_unlock(&sema->mutex);
}
void sema_signal(sema_t *sema){
pthread_mutex_lock(&sema->mutex);
sema->value++;
pthread_cond_signal(&sema->cond);
pthread_mutex_unlock(&sema->mutex);
}
void *reader(void *arg){
pthread_mutex_lock(&rmutex);
if(readercount == 0)
sema_wait(&rw_sema);
readercount++;
pthread_mutex_unlock(&rmutex);
printf("reading\n");
sleep(1); // 假设2s执行完读操作
pthread_mutex_lock(&rmutex);
readercount--;
if(readercount == 0)
sema_signal(&rw_sema);
pthread_mutex_unlock(&rmutex);
}
void *writer(void *arg){
sema_wait(&rw_sema);
printf("writing\n");
sleep(1); // 假设2s执行完写操作
sema_signal(&rw_sema);
}
int main(){
pthread_t reader_tid[SIZE], writer_tid[SIZE];
int m=0, n=0;
char order[] = "RRWRWRRRRW"; // 表示读写者进入顺序 R:reader,W:writer
sema_init(&rw_sema, 1);
pthread_mutex_init(&rmutex, NULL);
for(int i=0; i<sizeof(order); i++){
if(order[i] == 'R')
pthread_create(&reader_tid[m++], NULL, reader, NULL);
else if(order[i] == 'W')
pthread_create(&writer_tid[n++], NULL, writer, NULL);
}
pthread_join(reader_tid[m-1], NULL);
pthread_join(writer_tid[n-1], NULL); // 等待最后一个读者和写者执行完毕
}
存在问题
reader和writer是穿插进入,但是会因为之前已经有读者在读,后续读者进入时不需要等待,导致所有读者先执行,写者最后执行,因此造成写者饿死现象。
写者饥饿问题解决
思路:新增信号量writer_sema实现相对地写优先。
typedef struct{
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
}sema_t;
int readercount=0;
sema_t rw_sema, writer_sema;
pthread_mutex_t rmutex;
同时,读者写者线程函数作出适当改进。
写者线程函数:
void *writer(void *arg){
sema_wait(&writer_sema);
sema_wait(&rw_sema);
printf("writing\n");
sleep(1); // 假设2s执行完写操作
sema_signal(&rw_sema);
sema_signal(&writer_sema);
}
读者线程函数
void *reader(void *arg){
sema_wait(&writer_sema);
pthread_mutex_lock(&rmutex);
if(readercount == 0)
sema_wait(&rw_sema);
readercount++;
pthread_mutex_unlock(&rmutex);
sema_signal(&writer_sema);
printf("reading\n");
sleep(1); // 假设2s执行完读操作
pthread_mutex_lock(&rmutex);
readercount--;
if(readercount == 0)
sema_signal(&rw_sema);
pthread_mutex_unlock(&rmutex);
}
程序代码
#include<stdio.h>
#include<stdbool.h>
#include<pthread.h>
#include<unistd.h>
#define SIZE 100
typedef struct{
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
}sema_t;
int readercount=0;
sema_t rw_sema, writer_sema;
pthread_mutex_t rmutex;
void sema_init(sema_t *sema, int value){
sema->value = value;
pthread_cond_init(&sema->cond, NULL);
pthread_mutex_init(&sema->mutex, NULL);
}
void sema_wait(sema_t *sema){
pthread_mutex_lock(&sema->mutex);
if(sema->value <= 0)
pthread_cond_wait(&sema->cond, &sema->mutex);
sema->value--;
pthread_mutex_unlock(&sema->mutex);
}
void sema_signal(sema_t *sema){
pthread_mutex_lock(&sema->mutex);
sema->value++;
pthread_cond_signal(&sema->cond);
pthread_mutex_unlock(&sema->mutex);
}
void *reader(void *arg){
sema_wait(&writer_sema);
pthread_mutex_lock(&rmutex);
if(readercount == 0)
sema_wait(&rw_sema);
readercount++;
pthread_mutex_unlock(&rmutex);
sema_signal(&writer_sema);
printf("reading\n");
sleep(1); // 假设2s执行完读操作
pthread_mutex_lock(&rmutex);
readercount--;
if(readercount == 0)
sema_signal(&rw_sema);
pthread_mutex_unlock(&rmutex);
}
void *writer(void *arg){
sema_wait(&writer_sema);
sema_wait(&rw_sema);
printf("writing\n");
sleep(1); // 假设2s执行完写操作
sema_signal(&rw_sema);
sema_signal(&writer_sema);
}
int main(){
pthread_t reader_tid[SIZE], writer_tid[SIZE];
int m=0, n=0;
char order[] = "RRWRWRRRRW"; // 表示读写者进入顺序 R:reader,W:writer
sema_init(&rw_sema, 1);
sema_init(&writer_sema, 1);
pthread_mutex_init(&rmutex, NULL);
for(int i=0; i<sizeof(order); i++){
if(order[i] == 'R')
pthread_create(&reader_tid[m++], NULL, reader, NULL);
else if(order[i] == 'W')
pthread_create(&writer_tid[n++], NULL, writer, NULL);
}
pthread_join(reader_tid[m-1], NULL);
pthread_join(writer_tid[n-1], NULL); // 等待最后一个读者和写者执行完毕
}
改进原理:
当进入顺序为R1->R2->W->R3->R4…时,R1进来之后R2再来就不需要等待rw_sema,而R2在拿到管道读权限之后会释放writer_sema,此时当后续写着W进来时,会先P(writer_sema),拿到等待权,后续R3、R4…再来时因为等不到writer_sema因此无法无障碍进入管道,当最后的R2执行完毕后将rw_sema释放,writer线程函数继续执行,就可以实现写着相对公平。
测试用例同上,读者可自行运行程序验证。
作业1 吃水果问题
问题描述
桌上有一空盘,只允许存放一个水果。爸爸专向盘中放橙子,妈妈专向盘中放苹果,女儿专等吃橙子,儿子专等吃苹果。规定当盘空时一次只能放一个水果供吃者自用,请用PV操作实现爸爸、妈妈、女儿、儿子四个并发进程的同步。
问题解决
信号量
//条件变量实现
pthread_cond_t empty, apple, orange;
pthread_mutex_t mutex;
int empty_num = 1, apple_num = 0, orange_num = 0;
父亲线程函数
void *father(void *arg){
// printf("father\n");
while(1){
pthread_mutex_lock(&mutex);
if(empty_num == 0)
pthread_cond_wait(&empty, &mutex);
empty_num = 0;
orange_num = 1;
printf("Papa puts an orange.\n");
sleep(2); // 假设放橙子操作需要2s
pthread_cond_signal(&orange);
pthread_mutex_unlock(&mutex);
}
}
母亲线程函数
void *mather(void *arg){
// printf("mather\n");
while(1){
pthread_mutex_lock(&mutex);
if(empty_num == 0)
pthread_cond_wait(&empty, &mutex);
empty_num = 0;
apple_num = 1;
printf("Mama puts an apple.\n");
sleep(2); // 假设放苹果操作需要2s
pthread_cond_signal(&apple);
pthread_mutex_unlock(&mutex);
}
}
女儿线程函数
void *daughter(void *arg){
// printf("daughter\n");
while(1){
pthread_mutex_lock(&mutex);
if(orange_num == 0)
pthread_cond_wait(&orange, &mutex);
empty_num = 1;
orange_num = 0;
printf("Daughter eats an orange.\n");
sleep(2); // 假设拿橙子操作需要2s
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mutex);
}
}
儿子线程函数
void *son(void *arg){
// printf("son\n");
while(1){
pthread_mutex_lock(&mutex);
if(apple_num == 0)
pthread_cond_wait(&apple, &mutex);
empty_num = 1;
apple_num = 0;
printf("Son eats an apple.\n");
sleep(2); // 假设拿苹果操作需要2s
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mutex);
}
}
程序代码
#include<stdio.h>
#include<pthread.h>
//sleep头文件
#include<unistd.h>
//条件变量实现
pthread_cond_t empty, apple, orange;
pthread_mutex_t mutex;
int empty_num = 1, apple_num = 0, orange_num = 0;
void *father(void *arg){
// printf("father\n");
while(1){
pthread_mutex_lock(&mutex);
if(empty_num == 0)
pthread_cond_wait(&empty, &mutex);
empty_num = 0;
orange_num = 1;
printf("Papa puts an orange.\n");
sleep(2); // 假设放橙子操作需要2s
pthread_cond_signal(&orange);
pthread_mutex_unlock(&mutex);
}
}
void *mather(void *arg){
// printf("mather\n");
while(1){
pthread_mutex_lock(&mutex);
if(empty_num == 0)
pthread_cond_wait(&empty, &mutex);
empty_num = 0;
apple_num = 1;
printf("Mama puts an apple.\n");
sleep(2); // 假设放苹果操作需要2s
pthread_cond_signal(&apple);
pthread_mutex_unlock(&mutex);
}
}
void *daughter(void *arg){
// printf("daughter\n");
while(1){
pthread_mutex_lock(&mutex);
if(orange_num == 0)
pthread_cond_wait(&orange, &mutex);
empty_num = 1;
orange_num = 0;
printf("Daughter eats an orange.\n");
sleep(2); // 假设拿橙子操作需要2s
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mutex);
}
}
void *son(void *arg){
// printf("son\n");
while(1){
pthread_mutex_lock(&mutex);
if(apple_num == 0)
pthread_cond_wait(&apple, &mutex);
empty_num = 1;
apple_num = 0;
printf("Son eats an apple.\n");
sleep(2); // 假设拿苹果操作需要2s
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mutex);
}
}
int main(){
pthread_t father_tid, mather_tid, daughter_tid, son_tid;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&empty, NULL);
pthread_cond_init(&apple, NULL);
pthread_cond_init(&orange, NULL);
pthread_create(&father_tid, NULL, father, NULL);
pthread_create(&mather_tid, NULL, mather, NULL);
pthread_create(&daughter_tid, NULL, daughter, NULL);
pthread_create(&son_tid, NULL, son, NULL);
pthread_join(father_tid, NULL);
pthread_join(mather_tid, NULL);
pthread_join(daughter_tid, NULL);
pthread_join(son_tid, NULL);
return 0;
}
运行结果读者自行验证。
作业二 过桥问题
问题描述
假设有个南北向的桥,仅能容同方向的人顺序走过,相对方向的两个人则无法通过。现在桥南北端都有过桥人。现把每个过桥人当成一个进程,用P,V操作实现管理。
问题解决
信号量
typedef struct{
int value; // 资源数
pthread_cond_t cond;
pthread_mutex_t mutex;
}sema_t;
sema_t bridge_empty_sema, sn_sema;
pthread_mutex_t smutex, nmutex;
int south_num=0, north_num=0;
朝南走的线程函数
void *go_south(void *arg){
sema_wait(&sn_sema);
pthread_mutex_lock(&smutex);
if(south_num == 0)
sema_wait(&bridge_empty_sema);
south_num++;
pthread_mutex_unlock(&smutex);
sema_signal(&sn_sema);
printf("Going south.\n");
sleep(1); // 假设每个人用1s走完桥
pthread_mutex_lock(&smutex);
south_num--;
if(south_num == 0)
sema_signal(&bridge_empty_sema);
pthread_mutex_unlock(&smutex);
}
朝北走的线程函数
void *go_north(void *arg){
sema_wait(&sn_sema);
pthread_mutex_lock(&nmutex);
if(north_num == 0)
sema_wait(&bridge_empty_sema);
north_num++;
pthread_mutex_unlock(&nmutex);
sema_signal(&sn_sema);
printf("Going north.\n");
sleep(1);
pthread_mutex_lock(&nmutex);
north_num--;
if(north_num == 0)
sema_signal(&bridge_empty_sema);
pthread_mutex_unlock(&nmutex);
}
程序代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#define SIZE 100
typedef struct{
int value; // 资源数
pthread_cond_t cond;
pthread_mutex_t mutex;
}sema_t;
sema_t bridge_empty_sema, sn_sema;
pthread_mutex_t smutex, nmutex;
int south_num=0, north_num=0;
void sema_init(sema_t *sema, int value){
sema->value = value;
pthread_cond_init(&sema->cond, NULL);
pthread_mutex_init(&sema->mutex, NULL);
}
void sema_wait(sema_t *sema){
pthread_mutex_lock(&sema->mutex);
if(sema->value <= 0)
pthread_cond_wait(&sema->cond, &sema->mutex);
sema->value--;
pthread_mutex_unlock(&sema->mutex);
}
void sema_signal(sema_t *sema){
pthread_mutex_lock(&sema->mutex);
sema->value++;
pthread_cond_signal(&sema->cond);
pthread_mutex_unlock(&sema->mutex);
}
void *go_south(void *arg){
sema_wait(&sn_sema);
pthread_mutex_lock(&smutex);
if(south_num == 0)
sema_wait(&bridge_empty_sema);
south_num++;
pthread_mutex_unlock(&smutex);
sema_signal(&sn_sema);
printf("Going south.\n");
sleep(1); // 假设每个人用1s走完桥
pthread_mutex_lock(&smutex);
south_num--;
if(south_num == 0)
sema_signal(&bridge_empty_sema);
pthread_mutex_unlock(&smutex);
}
void *go_north(void *arg){
sema_wait(&sn_sema);
pthread_mutex_lock(&nmutex);
if(north_num == 0)
sema_wait(&bridge_empty_sema);
north_num++;
pthread_mutex_unlock(&nmutex);
sema_signal(&sn_sema);
printf("Going north.\n");
sleep(1);
pthread_mutex_lock(&nmutex);
north_num--;
if(north_num == 0)
sema_signal(&bridge_empty_sema);
pthread_mutex_unlock(&nmutex);
}
int main(){
pthread_t south_tid[SIZE], north_tid[SIZE];
int m=0, n=0;
char order[] = "NSNSNNNSSN"; // 表示桥两边来人的顺序 S:south N:north
sema_init(&bridge_empty_sema, 1);
sema_init(&sn_sema, 1);
pthread_mutex_init(&smutex, NULL);
pthread_mutex_init(&nmutex, NULL);
for(int i=0; i<sizeof(order); i++){
if(order[i] == 'S')
pthread_create(&south_tid[m++], NULL, go_south, NULL);
else if(order[i] == 'N')
pthread_create(&north_tid[n++], NULL, go_north, NULL);
}
pthread_join(south_tid[m-1], NULL);
pthread_join(north_tid[n-1], NULL); // 等待最后一个执行完毕
return 0;
}