【操作系统】第四次作业——解决写者饥饿问题、吃水果问题、过桥问题

作业0 解决管道读者写者饥饿问题

问题描述

写者:可以读,也可以写;读者:只读数据,不可写;多个读者可以同时读;写者修改数据时,其他写者和读者不可访问数据。
即:

先进入后进入是否可执行
RR
RW×
WR×
WW×

最终尝试

所用到信号量

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;
}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值