《操作系统---PV操作》(同步与互斥)

news2024/11/15 19:45:07

一、练习题

面包师有很多面包,由n名销售人员推销。每名顾客进店后按序取一个号,并且等待叫号,当一名销售人员空闲时,就按序叫下一个号。可以用两个整型变量来记录当前的取号值和叫号值,试设计一个使销售人员和顾客同步的算法。

分析:取号和叫号之间应该是一个同步关系吧,即我取完号了,销售人员后续才能喊。然后还有关于每个号,一个顾客拿了之后就不能再给其他顾客了,所以对于一个单一的号码,顾客之间对其访问应该是互斥的。而对于销售人员来说亦是如此,如果有一个销售小哥喊了号,就不允许其他小哥喊一样的号了,因此这个也是互斥的)

semaphore mutex_i=1,mutex_j=1;
int i=0,j=0;

Consumer(){
    //走进面包店
    P(mutex_i);    //互斥的访问号码i(所取的号码)
    i++;
    V(mutex_i);
}

Seller(){
    while(1){
        P(mutex_j);    //互斥的访问号码j(要喊的号码)
        if(j<i){    //此时有人在等待
            //叫号
            j++;
            V(mutex_j);
            //卖掉面包
        }
        else{    //这会儿没人要这个号(没有顾客在等待)
            V(mutex_j);
            //休息片刻
        }
    }
}

某工厂有两个生产车间和一个装配车间,两个生产车间分别生产A,B两种零件,装配车间的任务是把A,B两种零件组装成产品。两个生产车间每生产一个零件后,都要分别把它们送到专配车间的货架F上。F1存放零件A,F2存放零件B,F1和F2的容量均可存放10个零件。装配工人每次从货架上取一个零件A和一个零件B后组装成产品。请用PV操作进行正确管理。

分析:我们先想想能不能往之前的板子PV上套。如果我们单看一条线,这是不是其实就是一个缓冲区为10的消费者——生产者问题呢。那变成两条线一下多了什么问题呢?我们看到工人是需要既有A又有B才能组装成产品的。

semaphore full1=0,full2=0;
semaphore empty1=10,empty2=10;
semaphore mutex1=1,mutex=1;    //用于互斥地访问货架

PlantA(){
    //生产了一个产品A
    P(empty1);
    p(mutex1);
    //将产品A放到货架F1上
    V(mutex1);
    V(full1);
}

PlantB(){
    //生产了一个产品B
    P(empty2);
    p(mutex2);
    //将产品B放到货架F2上
    V(mutex2);
    V(full2);
}

Workshop(){
    P(full1);
    p(mutex1);
    //将产品A从货架F1上拿走
    V(mutex1);
    V(empty1);    //又产生了一格新的货架空间
    
    P(full2);
    p(mutex2);
    //将产品B从货架F2上拿走
    V(mutex2);
    V(empty2);    //又产生了一格新的货架空间

    //将AB组装成产品
}

某寺庙有小和尚、老和尚若干,有一水缸,由小和尚提水入缸供老和尚饮用。水缸可容10桶水,水取自同一井中。水井径窄,每次只能容一个桶取水。水桶总数为3个。每次入缸取水仅为1桶水,且不可同时进行。试给出有关从缸取水、入水的算法描述。 

分析:抓一下重点先,水缸一次只能容纳一个桶取水来着,所以水井其实是一个临界资源对吧(设置一个互斥信号量,初值为1),那我们再看水桶只有三个,那是不是不管你小和尚有多少个,最多只有三个人能拿到水桶对吧。(设置一个信号量cask,初值为3),然后看到老和尚那边是不是又变回生产者消费者问题了。水缸也是一个临界资源(设置一个互斥信号量,初值为1),然后就是熟悉的empty(初值为10)和full(初值为0)了。本题的关键是需要将散乱的同步和互斥关系串联起来,首先,得是水缸里有空余容量时,小和尚才需要“拎”桶起来去打水,从他拎起来桶的那一刻起,场上的桶数量就少1了,紧接着互斥地(交替地)打完水。再跑回来互斥地(交替地)倒入水缸。还有一点呢,就是作为德高望重的老和尚,咱不能那么粗鲁,咱也是得拿一个桶水打出来再喝,不能一头扎进去游泳啊。

semaphore cask=3;
semaphore well=1,jar=1;
semaphore full=0,empty=10;

Mock(){
    while(1){
        P(empty);    //水缸有空余位置的时候,才需要打水
        P(cask);    //拿起来一个桶
        P(well);    //互斥地访问水井
        //从水井打水
        V(well);
        P(jar);    //互斥地访问水缸
        //将水倒入水缸中
        V(jar); 
        V(full);
        V(cask);
    }  
}

Elder Mock(){
    while(1){
        P(full);
        P(cask);
        P(jar);    //互斥地访问水缸
        //从水缸里打一桶水
        V(jar);
        V(empty);
        V(cask);//喝掉一桶水,水缸的容量加一
    } 
}

如下图所示,三个合作进程P1,P2,P3,它们都需要通过同一设备输入各自的数据a,b,c,该输入设备必须互斥地使用,而且其第一个数据必须由P1进程读取,第二个数据必须由P2进程读取,第三个数据必须由P3进程读取。然后,三个进程分别对输入数据进行下列计算: 

P1:x=a+b 
P2:y=a*b
P3:z=y+c-a

最后,P进程通过所连接的打印机将计算结果x,y,z的值打印出来。请用信号量实现它们的同步。 

分析: 这个就比较简单啦,就是中间三对同步关系嘛,那我们就设置三个型号量S1(1)、S2(0)、S3(0),因为这样就不会同时使用输入设备了(那也就不用再多设置一个互斥信号量了)。然后我们再看图!只有P1这个老大能用打印机,那该怎么知道P2、P3中的操作已经完成了呢,当然还是要设置信号量啦(Sy,Sz初值都为0)。再然后我就中陷阱了,丫的abc还是分开输入的,那还得再来个Sb(我真是sb,初值也为0)。

semaphore S1=1,S2=0,S3=0;
semaphore Sy=0,Sz=0,Sb=0;

P1(){
    P(S1);
    从输入设备输入数据a;
    V(S2);
    P(Sb);
    x=a+b;
    P(Sy)
    P(Sz);
    使用打印机打印出x,y,2的结果;
}

P2(){
    P(S2);
    从输入设备输入数据b;
    V(S3);
    V(Sb);
    y=a*b;
    V(Sy);
    V(Sy);
}

P3(){
    P(S3);
    从输入设备输入数据c;
    P(Sy);
    z=y+c-a;
    V(Sz);
}

有桥如下图所示。车流方向如箭头所示。回答如下问题
1)假设桥上每次只能有一辆车行驶,试用信号灯的PV操作实现交通管理。
2)假设桥上不允许两车交会,但允许同方向多辆车一次通过(桥上可有多辆同方向行驶的车)。试用信号灯的PV操作实现桥上的交通管理。 

分析:毋庸置疑桥肯定是一个临界资源对吧,需要设置一个互斥信号量bridge,这第一题就做完咯,再看第二题,只允许同方向多辆车通过,那意思是不是需要给个计数器,只有一方真的没车了,才允许另一边发车。是不是就感觉题目其实就变成了多读者——一写者的变体了。

semaphore bridge=1;

North(){
    P(bridge);
    //行驶车辆;
    V(bridge);
}

South(){
    P(bridge);
    //行驶车辆;
    V(bridge);
}
semaphore bridge=1;
semaphore NtoS=0,StoN=0;
int cnt1=0,cnt2=0;

North(){
    while(1){
        P(NtoS);    //桥的每一边对于每一个cnt是互斥使用的,为了避免给桥加锁两次。
        if(cnt1==0)
            P(bridge);    //桥两边对桥的使用是互斥的
        cnt1++;
        V(NtoS);
        
        P(NtoS);
        cnt1--;
        if(cnt1==0)
            V(bridge);    //只有当一边真的没有车想要过桥的时候,才会让出使用权
        V(NtoS);
    }
}

South(){
    while(1){
        P(StoN);
        if(cnt2==0)
            P(bridge);
        cnt2++;
        V(StoN);
        
        P(StoN);
        cnt2--;
        if(cnt2==0)
            V(bridge);
        V(StoN);
    }
}

假设有两个线程(编号为0和1)需要去访问同一个共享资源,为避免竞争状态的问题,我们必须实现一种互斥机制,使得在任何时候只能有一个线程访问这个资源。假设有如下一段代码:

bool flag[2];             //flag数组,初始化为FALSE
Enter_Critical_section(int my_thread_id,int other_thread_id){
    while(flag[other_thread_id]==TRUE);         //空循环语句
    flag[my thread_id]=TRUE;
}

Exit_Critical_Section(int my_thread_id,int other_thread_id){
    flag[my_thread_id]=FALSE;
}

当一个线程想要访问临界资源时,就调用上述的这两个函数。

例如,线程0的代码可能是这样的:

Enter_Critical_Section(0,1);

使用这个资源;

Exit_Critical_Section(0,1);

做其他的事情;
试问:
1)以上的这种机制能够实现资源互访问吗?为什么?
2)若把Enter_Critical_Section函数中的两条语句互换位置,可能会发生死锁吗? 

分析:语句都很简单蛤,就是不停的问问问看看有没有别人在使用这个共享资源,问一圈没有人用的话,就自己用。看起来貌似是实现了互斥的使用啊,但是问也是要时间的啊,万一大家刚好一块问的,又刚好擦肩而过的时候都没有人在使用,那这不是就没有达成想要的效果了吗。

然后看回死锁,死锁是什么呢? 

死锁(Deadlock)指的是多个进程(或线程)在运行过程中因争夺资源而造成的一种僵局。在这种僵局中,每个进程都在等待其他进程释放资源,但没有任何一个进程能够继续执行,因为它们需要的资源都被其他进程占用着。

死锁的四个必要条件:

  1. 互斥条件(Mutual Exclusion):资源不能被多个进程同时使用,只能由一个进程使用。
  2. 占有和等待条件(Hold and Wait):进程至少持有一个资源,并且在等待其他进程释放它们持有的资源。
  3. 不可剥夺条件(No Preemption):已经分配给一个进程的资源,在该进程使用完之前,不能被强行夺走。
  4. 循环等待条件(Circular Wait):存在一种进程资源的循环等待关系,即进程A等待B的资源,B等待C的资源,...,形成一个循环。

这不是一看,包会的嘛。

参考答案

1)这种机制不能实现资源的互访问。考虑如下情形:
①初始化时,flag数组的两个元素值均为FALSE。
②线程0先执行,执行while循环语句时,由于flag[1]为FALSE,所以顺利结束,不会被卡住。假设这时来了一个时钟中断,打断了它的运行。
③线程1去执行,执行while循环语句时,由于flag[0]为FALSE,所以顺利结束,不会被卡住,然后进入临界区。
④后来当线程0再执行时,也进入临界区,这样就同时有两个线程在临界区。
总结:不能成功的根本原因是无法保证Enter_Critical_Section()函数执行的原子性。我们从上面的软件实现方法中可以看出,对于两个进程间的互,最主要的问题是标志的检查和修改不能作为一个整体来执行,因此容易导致无法保证互斥访问的问题。


2)可能会出现死锁。考虑如下情形:
①初始化时,flag数组的两个元素值均为FALSE
②线程0先执行,flag[0]为TRUE,假设这时来了一个时钟中断,打断了它的运行。
③线程1去执行,flag[1]为TRUE,在执行while循环语句时,由于flag[0]=TRUE,所以在这个地方被卡住,直到时间片用完。
④线程0再执行时,由于flag[1]为TRUE,它也在while循环语句的地方被卡住,因此这两个线程都无法执行下去,从而死锁。

设自行车生产线上有一个箱子,其中有N个位置(N>3),每个位置可存放一个车架或一个车轮;又设有3名工人,其活动分别为:

工人1活动:                              
do{                                                           
    加工一个车架;                      
    车架放入箱中;                         
}while(1) 
   
工人2活动:
do{
    加工一个车轮;
    车轮放入箱中;
}while(1)  

工人3活动:   
do{
    箱中取一个车架;  
    箱中取二个车轮;
    组装为一台车;                
}while(1)

试分别用信号量与PV操作实现三名工人的合作,要求解中不含死锁。

分析:很容易就可以看出工人3明显得是排在工人1和工人2之后的对吧。那其实这不本质上还是生产者、消费者问题吗?最开始我我刷刷刷就写出了下面的代码。不过突然看到一句话,要求解中不含死锁?欸~难道在这过程中可能会出现死锁吗?(要是工人一做事情比较快,箱子里面全部都是轮子。亦或者是工人二做事情比较快,那箱子里都是轮子,导致工人三不就组装不成自行车,也就释放不了空间,于是工人一和工人二都只能在阻塞状态。这不就是死锁了吗?)

但其实也很好解决就是空位置的设计,我们让车轮子最多最多只能生产n-2个,同样的车架子最多最多只能生产n-1个,那问题不就迎刃而解了吗。

semaphore empty = n;      // 箱子的空位置数
semaphore empty1 = n-2;
semaphore empty2 = n-1;
semaphore wheel = 0;      // 车轮计数信号量
semaphore frame = 0;      // 车架计数信号量
semaphore mutex = 1;      // 互斥信号量

// 工人1:制造车架
Worker1() {
    do {
        // 制造好了一个车架
        P(empty1);         // 检测是否已经达到最大数
        P(empty);          // 等待空位置
        P(mutex);          // 进入临界区
        // 将车架放入箱中
        V(mutex);          // 离开临界区
        V(frame);          // 增加车架信号量
    } while (true);
}

// 工人2:制造车架
Worker2() {
    do {
        // 制造好了一个车轮
        P(empty2);         // 检测是否已经达到最大数
        P(empty);          // 等待空位置
        P(mutex);          // 进入临界区
        // 将车轮放入箱中
        V(mutex);          // 离开临界区
        V(wheel);          // 增加车轮信号量
    } while (true);
}

// 工人3:组装自行车
Worker3() {
    do {
        P(frame);          // 等待一个车架
            P(mutex);          // 进入临界区
            // 取出一个车架
            V(mutex);          // 离开临界区
        V(empty);
        V(empty1);
        
        P(wheel);          // 等待第一个车轮
        P(wheel);          // 等待第二个车轮
            P(mutex);          // 进入临界区
            // 取出两个轮子
            V(mutex);          // 离开临界区
        V(empty);          // 释放一个空位置
        V(empty);          // 释放第二个空位置
        V(empty2);          
        V(empty2);          
        // 将其组装成一辆自行车
    } while (true);
}

设P,Q,R共享一个缓冲区,P,Q构成一对生产者-消费者,R既为生产者又为消费者,若缓冲区为空,则可以写入;若缓冲区不空,则可以读出。使用P,V操作实现其同步。

分析:要实现P,Q显然是很简单的,就是套个板子就好了。可这个R既为生产者又为消费者,该怎么处理呢?

它看到有东西可以读出来对吧,没东西它也可以写进去。那它很厉害呀,没有什么可以干预它的,我们只要保证和别的进程互斥就好了呀。

semaphore mutex = 1;  // 互斥信号量,用于保护缓冲区
semaphore empty = 1;  // 缓冲区的空位数
semaphore full = 0;   // 缓冲区的已填充项数

P() {  // 生产者
    while (1) {
        P(empty);     // 等待空位
        P(mutex);     // 进入临界区
        //Product One
        V(mutex);     // 退出临界区
        V(full);      // 增加已填充项数
    }
}

Q() {  // 消费者
    while (1) {
        P(full);      // 等待有产品
        P(mutex);     // 进入临界区
        //Consume One
        V(mutex);     // 退出临界区
        V(empty);     // 增加空位
    }
}

R() {  // 双面人
    if(empty==1){
        P(empty);     // 等待空位
        P(mutex);     // 进入临界区
        //Product One
        V(mutex);     // 退出临界区
        V(full);      // 增加已填充项数
    }

    if(full==1){
        P(full);      // 等待有产品
        P(mutex);     // 进入临界区
        //Consume One
        V(mutex);     // 退出临界区
        V(empty);     // 增加空位
    }
}

注:我个人理解觉得该题王道答案不太好其实,拿信号量当作判断条件应该是不太好的吧。

理发店里有一位理发师、一把理发椅和n把供等候理发的顾客坐的椅子。若没有顾客理发师便在理发椅上睡觉,一位顾客到来时,顾客必须叫醒理发师,若理发师正在理发时又有顾客来到,若有空椅子可坐,则坐下来等待,否则就离开。试用P,V操作实现,并说明信号量的定义和初值。

分析:首先对于顾客来说,这个n把椅子是不是其实就是缓冲区,只不过这题不一样的是如果此时没有一个人在等待的话,你可以把睡觉的那个臭小子叫起来为你服务;如果有人在等待的话,则判断是否还有地方坐,有地方坐就坐下来等,没地方坐就只能灰溜溜的离开了。

semaphore barber = 0;//表示理发师是否正在工作。初始值为0,表示理发师一开始在睡觉(不工作)。
semaphore consumers = 0;//表示当前在理发店里等待的顾客数量。初始值为0。
semaphore mutex = 1;//互斥信号量,用于保护临界区内共享资源(这里是line变量)的访问。初始值为1,表示没有其他同步需求,只有一个理发师。

int line = 0;//表示当前理发店里的顾客数量(包括正在理发的和等待的)。
int chairs = n;//表示理发店里供顾客等候的椅子数量。

Barber(){
    while(1){
        P(comsumers);
        P(mutex);
        line--;
        V(barber);
        V(mutex);
        //Tony 工作中
}

Consumer(){
    P(mutex);
    if(line<chairs){
        line++;
        V(comsumers);
        V(mutex);
        P(barber);
        //叫Tony来剪一个帅气的头型
    }else{
        V(mutex);
        //转身离开,有话说不出来~
    }
}

假设一个录像厅有1,2,3三种不同的录像片可由观众选择放映,录像厅的放映规则如下:
1)任意时刻最多只能放映一种录像片,正在放映的录像片是自动循环放映的,最后一名观众主动离开时结束当前录像片的放映。
2)选择当前正在放映的录像片的观众可立即进入,允许同时有多位选择同一种录像片的观众同时观看,同时观看的观众数量不受限制。
3)等待观看其他录像片的观众按到达顺序排队,当一种新的录像片开始放映时,所有等待观看该录像片的观众可依次序进入录像厅同时观看。用一个进程代表一个观众,要求:用信号量方法PV操作实现,并给出信号量定义和初始值。 

分析: 这题看上去真的是非常难的亚子,字居然这么多。让我们来捋捋逻辑影片是自动循环播放的那其实只要用三对PV操作就可以实现对不对,那么如何做到来看自己想看电影的人又恰好在播放的时候能走进去呢,是不是有点像多读者,第一个读者来的时候如果是是允许读的那就读,后续最后一个人走的时候关闭。(要理解PV其实就可以实现循环了哦,不需要天天while)

// 信号量定义
semaphore movie[3] = {1, 1, 1};  // 控制对录像片1、2、3的访问,初始值为1
semaphore playing = 1;            // 控制录像片的播放状态,初始值为1,表示可以开始播放

// 计数器定义
int c1 = 0;  // 当前观看录像片1的观众数量
int c2 = 0;  // 当前观看录像片2的观众数量
int c3 = 0;  // 当前观看录像片3的观众数量


void Filmgoer1() {
    P(movie[0]); // 进入临界区
    if (c1 == 0) {
        P(playing); // 如果没有观众,开始播放
    }
    c1++; // 增加观看人数
    V(movie[0]); // 退出临界区

    // Watching movie
    // 模拟观看时间

    P(movie[0]); // 进入临界区
    c1--; // 减少观看人数
    if (c1 == 0) {
        V(playing); // 如果是最后一位观众,停止播放
    }
    V(movie[0]); // 退出临界区
}

void Filmgoer2() {
    P(movie[1]); // 进入临界区
    if (c2 == 0) {
        P(playing); // 如果没有观众,开始播放
    }
    c2++; // 增加观看人数
    V(movie[1]); // 退出临界区

    // Watching movie
    // 模拟观看时间

    P(movie[1]); // 进入临界区
    c2--; // 减少观看人数
    if (c2 == 0) {
        V(playing); // 如果是最后一位观众,停止播放
    }
    V(movie[1]); // 退出临界区
}

void Filmgoer3() {
    P(movie[2]); // 进入临界区
    if (c3 == 0) {
        P(playing); // 如果没有观众,开始播放
    }
    c3++; // 增加观看人数
    V(movie[2]); // 退出临界区

    // Watching movie
    // 模拟观看时间

    P(movie[2]); // 进入临界区
    c3--; // 减少观看人数
    if (c3 == 0) {
        V(playing); // 如果是最后一位观众,停止播放
    }
    V(movie[2]); // 退出临界区
}

设公共汽车上驾驶员和售票员的活动分别如下图所示。驾驶员的活动:启动车辆,正常行车,到站停车:售票员的活动:关车门,售票,开车门。在汽车不断地到站、停车、行驶的过程中,这两个活动有什么同步关系?用信号量和PV操作实现它们的同步。

一组进程的执行顺序如下图所示,圆圈P1,P2,P3,P4,P5,P6表示进程,弧上的字母a,b,c,d,e,f,g,h表示同步信号量,请用P,V操作实现进程的同步。

分析:我们很容易就能看到这张图中有着许许多多的同步关系,那其实只需要仿照书中的代码,小小照猫画虎一下即可。

//一共有多少对同步关系是不是其实就只要看有多少根连线即可
semaphore a=b=c=d=e=f=g=h=0;

P1(){
	V(a);
	V(b);	
} 

P2(){
	P(a);
	V(c);
	V(d);
}

P3(){
	P(b);
	V(e);
	V(f);
}

P4(){
	P(e);
	P(c);
	V(g);
}

P5(){
	P(f);
	P(d);
	V(h);
}

P6(){
	P(h);
	P(g);
}

假设有3个抽烟者和1个供应者。每个抽烟者不停地卷烟并抽掉它,但要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草,第二个拥有纸,第三个拥有胶水。供应者无限提供三种材料,供应者每次将两种材料放到桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者一个信号告诉已完成,此时供应者就将另外两种材料放到桌上,如此重复,让3个抽烟者轮流抽烟。

有3个进程P1、P2、P3合作处理数据,P从输入设备读数据到缓冲区,缓冲区可存1000个字。P1和P2的功能一样,都是从缓冲区取出数据并计算,再打印结果。请用信号量的P,V操作实现。其中,语句read()从输入设备读入20个字到缓冲区;get()从缓冲区取出20个字;comp()计算40个字输出并得到结果的1个字;print()打印结果的2个字。

二、真题(往年)

【2009统考真题】三个进程P1,P2,P3互斥使用一个包含N(N>0)个单元的缓冲区。P1每次用produce()生成一个正整数并用put()送入缓冲区某一空单元;P2每次用getodd()从该缓冲区中取出一个奇数并用countodd()统计奇数个数;P3每次用geteven()从该缓冲区中取出一个偶数并用counteven()统计偶数个数。请用信号量机制实现这三个进程的同步与互斥活动,并说明所定义的信号量的含义(要求用伪代码描述)。

正如题目中所描述的其实已经相当明朗了对吧,就是生产者消费者的变体,但需要额外考虑的是生产出来的究竟是偶数还是奇数,然后再通知另外的两个进程来取走各自想要的数字。

semaphore mutex = 1;
semaphore empty = N;      // Number of empty slots
semaphore cntO = 0;      // Count of odd numbers
semaphore cntE = 0;      // Count of even numbers

P1() {
    while (true) {
        P(empty);          // Wait for an empty slot
        P(mutex);         // Enter critical section

        int number = Produce(); // Produce a number
        puts(number);     // Store the number in the buffer

        if (number % 2 != 0) { // Check if the number is odd
            V(cntO);       // Signal there is an odd number
        } else {
            V(cntE);       // Signal there is an even number
        }

        V(mutex);         // Exit critical section
    }
}

P2() {
    while (true) {
        P(cntO);          // Wait for an odd number
        P(mutex);         // Enter critical section

        int number = getsodd(); // Get odd number from buffer
        countodd();       // Count the odd number

        V(mutex);         // Exit critical section
        V(empty);         // Signal an empty slot
    }
}

P3() {
    while (true) {
        P(cntE);          // Wait for an even number
        P(mutex);         // Enter critical section

        int number = geteven(); // Get even number from buffer
        counteven();     // Count the even number

        V(mutex);         // Exit critical section
        V(empty);         // Signal an empty slot
    }
}

【2011统考真题】某银行提供1个服务窗口和10个供顾客等待的座位。顾客到达银行时,若有空座位,则到取号机上领取一个号,等待叫号。取号机每次仅允许一位顾客使用。当营业员空闲时,通过叫号选取一位顾客,并为其服务。顾客和营业员的活动过程描述如下:
 

cobegin
{
    process 顾客i
    {
        从取号机获取一个号码;
        等待叫号;
        获取服务;
    }
    process 营业员
    {
        While(TRUE)
        {
            叫号;
            为客户服务;
        }
    }
}coend

请添加必要的信号量和P,V[或wait(),signal()]操作,实现上述过程中的互斥与同步。要求写出完整的过程,说明信号量的含义并赋初值。

分析:做过很类似的叫号问题蛤,首先得给每个号码都套上一个信号量互斥的访问(因为一个号码只能由一个人拿到。然后分别设置两个整型变量用于记录当前已经取走的号码和当前已经喊到的号码。最后再看回座位其实也就是缓冲区对吧。

semaphore empty=10;
int i=0,j=0;
semaphore mutexi=1,mutexj=1;
semaphore serviceWindow=1;
cobegin
{
    process 顾客i
    {
        P(empty);
        P(mutexi);
        从取号机获取一个号码;
        i++;
        V(mutexi)
        P(serviceWindow);
        等待叫号;
        获取服务;
        V(empty)
    }

    process 营业员
    {
        While(TRUE)
        {
            P(mutexj);
            叫号;
            if(j>i){
                V(mutexj);
            }
            else{
                为客户服务;
                j++;
                V(serviceWindow);
                V(mutexj);
            }
        }
    }
}coend

【2013统考真题】某博物馆最多可容纳500人同时参观,有一个出入口,该出入口一次仅允许一人通过。参观者的活动描述如下:

cobegin
{
    参观者进程i
    {
        ...
        进门;
        ...
        参观;
        ...
        出门;
        ...
    }
}coend

请添加必要的信号量和P,V[或wait(),signal()]操作,以实现上述过程中的互斥与同步。要求写出完整的过程,说明信号量的含义并赋初值。

【2014统考真题】系统中有多个生产者进程和多个消费者进程,共享一个能存放1000件产品的环形缓冲区(初始为空)。缓冲区未满时,生产者进程可以放入其生产的一件产品,否则等待:缓冲区未空时,消费者进程可从缓冲区取走一件产品,否则等待。要求一个消费者进程从缓冲区连续取出10件产品后,其他消费者进程才可以取产品。请使用信号量P,V(wait(),signal())操作实现进程间的互斥与同步,要求写出完整的过程,并说明所用信号量的含义和初值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2073698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PyTorch深度学习模型训练流程的python实现:回归

回归的流程与分类基本一致&#xff0c;只需要把评估指标改动一下就行。回归输出的是损失曲线、R^2曲线、训练集预测值与真实值折线图、测试集预测值散点图与真实值折线图。输出效果如下&#xff1a; 注意&#xff1a;预测值与真实值图像处理为按真实值排序&#xff0c;图中呈现…

聚合智链已获道富环球投资,正式上线AI合约策略资金托管平台

全球最大的托管银行之一道富环球首次进军加密货币领域&#xff0c;聚合智链获得其投资支持&#xff0c;打造出全球领先的AI合约策略资金托管平台&#xff0c;将在2024年8月 28 日正式上线。 道富环球集团的总部位于美国&#xff0c;其成立于1792年&#xff0c;是一家专注于托管…

easypoi模板导出word多页导出加强版

说明 上一篇文章提到多页导出&#xff0c;但是后边发现一个问题&#xff0c;如果用同一个模板导出多页内容&#xff0c;我们去获取多页内容的时候&#xff0c;会发现全部都一样&#xff0c;举个例子&#xff1a; XWPFDocument document WordExportUtil.exportWord07(outputU…

深度学习入门-第4章-神经网络的学习

学习就是从训练数据中自动获取最优权重参数的过程。引入损失函数这一指标&#xff0c;学习的目的是找出使损失函数达到最小的权重参数。使用函数斜率的梯度法来找这个最小值。 人工智能有两派&#xff0c;一派认为实现人工智能必须用逻辑和符号系统&#xff0c;自顶向下看问题…

java-Mybatis框架

简介 MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQl、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO&#xff08;Plain Old Java Obje…

DFS 算法:全排列问题

我的个人主页 {\large \mathsf{{\color{Red} 我的个人主页} } } 我的个人主页 往 {\color{Red} {\Huge 往} } 往 期 {\color{Green} {\Huge 期} } 期 文 {\color{Blue} {\Huge 文} } 文 章 {\color{Orange} {\Huge 章}} 章 DFS 算法&#xff1a;记忆化搜索 此系列更新频繁&…

k8s中service对象

文章目录 一、Service简介Service和kube-proxy的作用与区别Service的工作过程kube-proxy的工作过程总结 二、具体实践ClusterIPClusterIP 基本概念应用场景 NodePortNodePort 简介应用场景 ExternalName简介应用场景 一、Service 简介 Kubernetes (k8s) 中的 Service 对象是一…

使用redis设计延迟队列

目录 延迟队列概念与重要性 定义&#xff1a;延迟队列的基本概念 重要性&#xff1a;延迟队列在处理异步任务中的关键作用 图表&#xff1a;延迟队列的工作流程图 ​编辑延迟队列设计案例 背景介绍 设计目标 系统架构 设计要点 现有物理拓扑 图表&#xff1a;有赞延迟…

GStreamer 简明教程(五):Pad 相关概念介绍,Pad Capabilities/Templates

系列文章目录 GStreamer 简明教程&#xff08;一&#xff09;&#xff1a;环境搭建&#xff0c;运行 Basic Tutorial 1 Hello world! GStreamer 简明教程&#xff08;二&#xff09;&#xff1a;基本概念介绍&#xff0c;Element 和 Pipeline GStreamer 简明教程&#xff08;三…

自修C++Primer----3.2标准库类型string

目录 1.String的相关操作 1.1拷贝初始化&&直接初始化 1.2显示创建临时对象 1.3读取string对象内容 1.4一次读取多个未知对象 1.5使用getline读取一整行内容 1.6size()的返回值size_type类型 1.7两个string对象比较 1.8string对象赋值 1.9两个string对象相加 1…

策略产品 ①算法逻辑

目录 一、机器学习与AI的关系 二、机器学习全流程 1. 问题定义 2. 数据处理 3. 特征工程 4. 模型训练 5. 模型评估 6. 模型应用 机器学习是AI的关键技术之一&#xff0c;是指机器从历史数据中学习规律&#xff0c;从而提升系统某个性能度量的过程。这篇文章&#xff0c;我们在作…

C Primer Plus第十四章编程练习,仅供参考

第十四章编程练习 第一个问题让我们改写复习题5&#xff0c;创建一个函数去计算一年到某个月份的天数&#xff0c;在一个结构数组中去存储相关数据。完整程序代码以及运行结果如下&#xff1a; #include<stdio.h> #include<string.h> #include<ctype.h> st…

当外接硬盘接入到macOS上,只读不可写时,应当格式化

当windows磁盘格式例如 NTFS 的硬盘接入到macOS上时&#xff0c;会发现无法新建文件夹&#xff0c;无法删除、重命名。原因是磁盘格式对不上macOS&#xff0c;需要进行格式化。格式化时请注意备份重要数据。具体做法如下&#xff0c;在macOS中找到磁盘工具&#xff0c;然后对磁…

【HTML】常用几种模拟动画效果【附源代码】

1. 模拟音频波纹加载效果 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthde…

计算机视觉编程

目录 灰色度 缩略图 拷贝粘贴区域 调整图像尺寸 旋转图像45 画图线、描点 灰色度 灰度是指图像中每个像素的亮度值&#xff0c;用来描述图像中各个像素的明暗程度。在计算机视觉中&#xff0c;灰度可以通过以下方式来计算&#xff1a; 1. 平均值法&#xff1a;将图像中每…

如何在程序中创建出多条线程

多线程是编程中的一个重要概念&#xff0c;它允许程序同时执行多个任务&#xff0c;每个任务可以看作是一个线程。在Java中&#xff0c;多线程尤为常见且强大&#xff0c;它通过允许程序在并发环境下运行&#xff0c;提高了程序的执行效率和响应速度。以下是对Java多线程的详细…

数学建模~~~预测方法--决策树模型

目录 0.直击重点 1.决策树概念 2.节点特征的选择算法 3.基尼系数的计算 4.决策树的分类 5.模型的搭建 6.模型的改进和评价 ROC曲线 参数调优 &#xfeff;GridSearch网格搜索 使用搜索结果重新建模 0.直击重点 这个文章&#xff0c;我们从三个维度进行说明介绍&#…

如何使用Python快速修改文件的标签(如何将歌词嵌入到音乐文件中,含歌词嵌入接口源码)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 Python与Music 📒📝 1. 初探音乐文件的标签📝 使用Python修改标签📝 将歌词嵌入音乐文件⚓️ 相关链接 ⚓️📖 介绍 📖 你是否曾经听过一首好听的歌曲,却发现它的标签信息(元数据信息)杂乱无章?甚至找不到歌词?…

【Remi Pi开发板镜像烧录】使用sd卡进行瑞米派镜像的烧录

烧录大典 按照《软件开发指南》4.2.1和4.2.2的顺序进行&#xff0c;具体烧录哪个镜像结合你自己的需求&#xff0c;每个镜像的区别参考以下链接 https://mbb.eet-china.com/forum/topic/143906_1_1.html Tera term界面全屏如下设置看着比较舒服 设置完之后setup->save-&g…

智能优化特征选择|基于鹦鹉优化(2024年新出优化算法)的特征选择(分类器选用的是KNN)研究Matlab程序 【优化算法可以替换成其他优化方法】

智能优化特征选择|基于鹦鹉优化&#xff08;2024年新出优化算法&#xff09;的特征选择&#xff08;分类器选用的是KNN&#xff09;研究Matlab程序 【优化算法可以替换成其他优化方法】 文章目录 一、PO基本原理PO基本原理基本流程示例应用 二、实验结果三、核心代码四、代码获…