【操作系统】第二章--进程的描述与控制--深入与解释(2)

深入解释之前可以先看相应的笔记理解→【操作系统】第二章–进程的描述与控制–笔记与理解(2)

第二章–进程的描述与控制–深入与解释(2)

三个经典案例的同步代码

生产者-消费者
  • 单生产者-单消费者-单缓冲区
// 核心代码
void *producer(void *p) {
    while(1) {
        sem_wait(&space);
        printf("Put a product\n");
        sem_post(&prod);
    }
    return NULL;
}
void *consumer(void *p) {
    while(1) {
        sem_wait(&prod);
        printf("Get a product\n");
        sem_post(&space);
    }
    return NULL;
}

// 完整代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
sem_t space,prod;

void *producer(void *p) {
    while(1) {
        sem_wait(&space);
        printf("Put a product\n");
        sem_post(&prod);
    }
    return NULL;
}
void *consumer(void *p) {
    while(1) {
        sem_wait(&prod);
        printf("Get a product\n");
        sem_post(&space);
    }
    return NULL;
}

int main(void) {

    sem_init(&space,0,1);
    sem_init(&prod,0,0);
    pthread_t tid[2];
    pthread_create(&tid[0],NULL,producer,NULL);
    pthread_create(&tid[1],NULL,consumer,NULL);
    sem_destroy(&space);
    sem_destroy(&prod);
    pthread_join(tid[0],NULL);
    
    return 0;
}


  • 单生产者-单消费者-多缓冲区
// 核心代码
void *producer(void *p) {
    while(1) {
        sem_wait(&space);
        printf("Put a product into Buffer[%d]\n",in);
        in = (in+1)%N;
        sem_post(&prod);
    }
    return NULL;
}
void *consumer(void *p) {
    while(1) {
        sem_wait(&prod);
        printf("Get a product from Buffer[%d]\n",out);
        out = (out+1)%N;
        sem_post(&space);
    }
    return NULL;
}

// 完整代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define N 10
sem_t space,prod;
int BUffer[N];
int in=0,out=0;

void *producer(void *p) {
    while(1) {
        sem_wait(&space);
        printf("Put a product into Buffer[%d]\n",in);
        in = (in+1)%N;
        sem_post(&prod);
    }
    return NULL;
}
void *consumer(void *p) {
    while(1) {
        sem_wait(&prod);
        printf("Get a product from Buffer[%d]\n",out);
        out = (out+1)%N;
        sem_post(&space);
    }
    return NULL;
}

int main(void) {

    sem_init(&space,0,N);
    sem_init(&prod,0,0);
    pthread_t tid[2];
    pthread_create(&tid[0],NULL,producer,NULL);
    pthread_create(&tid[1],NULL,consumer,NULL);
    sem_destroy(&space);
    sem_destroy(&prod);
    pthread_join(tid[0],NULL);

    return 0;
}


  • 多生产者-多消费者
// 核心代码
void *producer(void *p) {
    while(1) {
        sem_wait(&space);
        sem_wait(&buf);
        printf("Put a product into Buffer[%d]\n",in);
        in = (in+1)%N;
        sem_post(&prod);
        sem_post(&buf);
    }
    return NULL;
}
void *consumer(void *p) {
    while(1) {
        sem_wait(&prod);
        sem_wait(&buf);
        printf("Get a product from Buffer[%d]\n",out);
        out = (out+1)%N;
        sem_post(&space);
        sem_post(&buf);
    }
    return NULL;
}

// 完整代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define M 8
#define N 10
sem_t space,prod,buf;
int Buffer[N];
int in = 0, out = 0;

void *producer(void *p) {
    while(1) {
        sem_wait(&space);
        sem_wait(&buf);
        printf("Put a product into Buffer[%d]\n",in);
        in = (in+1)%N;
        sem_post(&prod);
        sem_post(&buf);
    }
    return NULL;
}
void *consumer(void *p) {
    while(1) {
        sem_wait(&prod);
        sem_wait(&buf);
        printf("Get a product from Buffer[%d]\n",out);
        out = (out+1)%N;
        sem_post(&space);
        sem_post(&buf);
    }
    return NULL;
}

int main(void) {
    
    sem_init(&space,0,N);
    sem_init(&prod,0,0);
    sem_init(&buf,0,1);
    pthread_t tid[M],tid2[M];
    for(int i = 0; i < M; i ++) {
        pthread_create(&tid[i],NULL,producer,NULL);
    }
    for(int i = 0; i < M; i ++) {
        pthread_create(&tid2[i],NULL,consumer,NULL);
    }
    sem_destroy(&space);
    sem_destroy(&prod);
    sem_destroy(&buf);
    pthread_join(tid[0],NULL);

    return 0;

}

读者-写者
  • 读者-写者-读者优先
// 核心代码
void *reader(void *p) {
    sem_wait(&srcount);
    readcount++;
    if(readcount == 1) {
        sem_wait(&sdata);
    }
    sem_post(&srcount);
    printf("Reading..\n");
    sem_wait(&srcount);
    readcount--;
    if(readcount == 0) {
        sem_post(&sdata);
    }
    sem_post(&srcount);
    return NULL;
}
void *writer(void *p) {
    sem_wait(&sdata);
    printf("Writing..\n");
    sem_post(&sdata);
    return NULL;
}

// 完整代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define M 10
sem_t sdata,srcount;
int readcount = 0;

void *reader(void *p) {
    sem_wait(&srcount);
    readcount++;
    if(readcount == 1) {
        sem_wait(&sdata);
    }
    sem_post(&srcount);
    printf("Reading..\n");
    sem_wait(&srcount);
    readcount--;
    if(readcount == 0) {
        sem_post(&sdata);
    }
    sem_post(&srcount);
    return NULL;
}
void *writer(void *p) {
    sem_wait(&sdata);
    printf("Writing..\n");
    sem_post(&sdata);
    return NULL;
}

int main(void) {

    sem_init(&sdata,0,1);
    sem_init(&srcount,0,1);
    pthread_t tid[M],tid2[M];
    for(int i = 0; i < M; i ++) {
        pthread_create(&tid[i],NULL,writer,NULL);
    }
    for(int i = 0; i < M; i ++) {
        pthread_create(&tid2[i],NULL,reader,NULL);
    }
    sem_destroy(&sdata);
    sem_destroy(&srcount);
    pthread_join(tid[0],NULL);

    return 0;
}


  • 读者-写者-写者优先
// 核心代码
void *reader(void *p) {
    sem_wait(&sdata);
    sem_wait(&srcount);
    sem_post(&sdata);
    printf("Reading..\n");    
    sem_post(&srcount);
    return NULL;
}
void *writer(void *p) {
    sem_wait(&sdata);
    for(int i = 0; i < N; i++) {
        sem_wait(&srcount);
    }
    printf("Writing..\n");
    for(int i = 0; i < N; i++) {
        sem_post(&srcount);
    }
    sem_post(&sdata);
    return NULL;
} 

// 完整代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define M 10
sem_t sdata,srcount;
int readcount = 0;

void *reader(void *p) {
    sem_wait(&srcount);
    readcount++;
    if(readcount == 1) {
        sem_wait(&sdata);
    }
    sem_post(&srcount);
    printf("Reading..\n");
    sem_wait(&srcount);
    readcount--;
    if(readcount == 0) {
        sem_post(&sdata);
    }
    sem_post(&srcount);
    return NULL;
}
void *writer(void *p) {
    sem_wait(&sdata);
    printf("Writing..\n");
    sem_post(&sdata);
    return NULL;
}

int main(void) {

    sem_init(&sdata,0,1);
    sem_init(&srcount,0,1);
    pthread_t tid[M],tid2[M];
    for(int i = 0; i < M; i ++) {
        pthread_create(&tid[i],NULL,writer,NULL);
    }
    for(int i = 0; i < M; i ++) {
        pthread_create(&tid2[i],NULL,reader,NULL);
    }
    sem_destroy(&sdata);
    sem_destroy(&srcount);
    pthread_join(tid[0],NULL);

    return 0;
}


哲学家就餐
  • 基本方案
#include<stdio.h>
#include<semaphore.h>
#include<pthread.h>
sem_t sfork[5];
void *philosopher(void*p) {
    int id=(int)p;
    while(1){
        printf("Thinking...\n");
        sem_wait(&sfork[id]);
        sem_wait(&sfork[(id+1)%5]);
        printf("Eating...\n");
        sem_post(&sfork[id]);
        sem_post(&sfork[(id+1)%5]);
    }
    return NULL;
}
int main() {
    for(int i=0;i<5;i++)
        sem_init(&sfork[i],0,1);
    pthread_t tid[5]];
    for(int i=0;i<5;i++)
        pthread_create(&tid[i],NULL,philosopher,(void*)i);
    for(int i=0;i<5;i++)
        sem_destroy(&sfork);
    pthread_join(tid[0],NULL);
}
  • 解决方案一
#include<stdio.h>
#include<semaphore.h>
#include<pthread.h>
sem_t sfork[5];
void *philosopher(void*p) {
    int id=(int)p;
    while(1){
        printf("Thinking...\n");
        if(id%2==0){
            sem_wait(&sfork[id]);
            sem_wait(&sfork[(id+1)%5]);
        }
        else{
            sem_wait(&sfork[(id+1)%5]);
            sem_wait(&sfork[id]);
        }
        printf("Eating...\n");
        sem_post(&sfork[id]);
        sem_post(&sfork[(id+1)%5]);
    }
    return NULL;
}
int main() {
    for(int i=0;i<5;i++)
    sem_init(&sfork[i],0,1);
    pthread_t tid[5]];
    for(inti=0;i<5;i++)
        pthread_create(&tid[i],NULL,philosopher,(void*)i);
    for(inti=0;i<5;i++)
        sem_destroy(&sfork);
    pthread_join(tid[0],NULL);
}
  • 解决方案二
#include<stdio.h>
#include<semaphore.h>
#include<pthread.h>
sem_t sfork[5];
void *philosopher(void*p) {
    int id=(int)p;
    while(1){
        printf("Thinking...\n");
        if(id%==4){
            sem_wait(&sfork[0]);
            sem_wait(&sfork[4]);
        }
        else{
            sem_wait(&sfork[id]);
            sem_wait(&sfork[id+1]);
        }
        printf("Eating...\n");
        sem_post(&sfork[id]);
        sem_post(&sfork[(id+1)%5]);
    }
    return NULL;
}
int main() {
    for(int i=0;i<5;i++)
        sem_init(&sfork[i],0,1);
    pthread_ttid[5]];
    for(int i=0;i<5;i++)
        pthread_create(&tid[i],NULL,philosopher,(void*)i);  
    for(int i=0;i<5;i++)
        sem_destroy(&sfork);
    pthread_join(tid[0],NULL);
}

理解三个案例

生产者-消费者模型所能体现的设计好处
  1. 解耦:从概念讲,耦合是指两个或两个以上的体系或者两种运动形式间通过互相作用而彼此影响以致联合起来的现象,那解耦实际上就是用数学方法将两种运动分解开来处理问题。那生产者-消费者模型是如何体现解耦的思想呢?举一个简单的例子,我们想换个吃饭的口味,就会想到点外卖,在没有各种外卖平台之前,大街上也不会到处都是外卖小哥的身影,我们想要点一份外卖,往往就是打电话直接到店里点一份餐。而接到点餐电话的老板也往往是接到电话、做菜做饭、送餐到顾客手里。那我们就会发现此时的老板太不容易了,首先得要自己洗菜切菜烹饪会做菜,还要在做好菜后自己打包装好,接着骑上自行车或者电动车自己去送餐,还要对全城的道路非常熟悉保证快速准确;当一个老板还要承包所有事物真的很辛苦,况且,如果这一单的外卖时间很长的话,会直接影响到下一单外卖的时间,如果慌张不小心在做菜的时候伤到自己,可能这一天的菜都做不了了。做饭和送餐这两个事物互相联系、互相影响,全都耦合在了一起;但是有了外卖小哥就不一样了,老板只用专注于把菜做好就可以了,送餐问题交给外卖小哥就可以了,职能清晰,效率自然就更高;这里我们可以把老板当成生产者,外卖小哥当成消费者,解决这一耦合的问题;
  2. 均衡消费者和生产者的能力:同样是举外卖的例子,如果老板做菜做的很快,但外卖小哥送餐时花费的时间很长,那么我们拿到外卖就会等很长的时间,而且最终等到的并不是老板真正想呈现出来的菜的样子,那我们或许就对这家菜品产生了不好的感觉;同样的,如果老板做菜比较慢,外卖小哥一直在店里等啊等,影响到点这份餐的顾客用餐时间不说,其他的订单也会被耽误,同样是一个问题。因为生产者-消费者模式已经将他的只能更加清晰地分离开来,所以解决起来也可以快速的对症下药;对于第一种消费者消耗(外卖小哥)的速度跟不上生产者生产(老板)的产能这一问题,我们就只用关注于消费者,可以多增派些外卖小哥,大家一人管一片自己最熟悉的区域,不仅迅速,准确率还高;对于第二种生产者(老板)产能跟不上消费者(外卖小哥)速度的问题,我们就只用关注于提高生产者产能这一方面,多雇些厨师,多开些分店就能很好地解决产能问题; 这样就可以解决生产者和消费者之间产能不均衡的问题。
读者-写者模型在生活中的应用
  • 读者-写者模型其实就是要遵循三个重要的点:读写互斥,写写互斥,读读不互斥;
  • 举一个很简单的例子,在平时的上课学习中,正在上课的老师往往会用到电脑和投影仪来辅助上课,对于投到屏幕上的内容,在教室中的同学都可以同时看到、同时接收新知识,这里就体现了读读不互斥,我读我的,你读你的,我们对内容都不进行改变,所以不存在互斥的问题;在进行翻转课堂的时候,有很多同学都将自己的PPT拷到了教室电脑上,当轮到自己的时候,讲解自己的PPT,这段时间中,电脑的使用权就交到了每一个正在讲课的同学身上,不可能说是,两个同学同时上去操作电脑,只有一对键盘鼠标,不允许两人同时操作,这就对应读者-写者模型中的写写互斥;在演示PPT的过程中,突然发现有一处错误,需要当场修改一下,当然不能直接在正在演示的PPT中直接修改,要先切换到PPT的编辑模式,对他重新进行编辑保存,再次打开PPT进行演示时,出现的内容才是修改过后的正确内容,这里就体现了读写互斥;
生产者与消费者问题中,producer()和consumer()中各有两个wait()操作和两个signal()操作,能否改变相邻操作的顺序,改变会带来什么影响?
  • producer()中:
    1. 将wait(empty)记为操作①
    2. wait(mutex)记为操作②
  • consumer()中:
    1. 将wait(full)记为操作③
    2. wait(mutex)记为操作④
  • producer()中:
    1. 将signal(mutex)记为操作⑤
    2. signal(full)记为操作⑥
  • consumer()中:
    1. 将signal(mutex)记为操作⑦
    2. signal(empty)记为操作⑧。
  1. wait():交换③和④,进程一开始,首先执行①,wait(empty)为真,继续执行;然后执行④,wait(mutex)为真,继续执行;然后执行②,wait(mutex)为假,生产者进程阻塞;然后执行③wait(full)为假,消费者进程阻塞,所以不能调换。
  2. signal():可以交换,但是会带来开销。交换⑤⑥,则⑥signal(full)会唤醒在等待可消费缓冲区的进程,执行consumer中 wait(full)操作为真,继续执行wait(mutex)为假,阻塞;但是⑤signal(mutex)会唤醒等待进入临界区的进程。所以可交换,但开销相对较大。
生产者与消费者问题中,如果缓冲区已满,交换生产者的wait()操作 ,且交换消费者的wait()操作,会带来什么影响?
  • 若此时缓冲区内已经放满产品,则empty=0,full=n.则生产者执行wait(mutex)使mutex变为0,再执行wait(empty),由于已经没有空闲缓冲区,因此生产者会被阻塞。由于生产者阻塞,因此切换回消费者进程,消费者进程执行wait(mutex),由于mutex为0,即生产者还没有释放对临界资源的锁,因此消费者也被阻塞。这就造成了生产者等待消费者释放空闲缓冲区,而消费者又等待生产者释放临界区的情况,生产者和消费者循环等待被对方唤醒,造成了“死锁”
同步与互斥之间的区别和联系
  • 所谓互斥,是指三部在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。所谓同步,是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的 某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。同步是一种更为复杂的互斥,而互斥是一种特殊的同步。也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)

猜你喜欢

转载自blog.csdn.net/weixin_44321600/article/details/107494387