zoukankan      html  css  js  c++  java
  • 进程经典问题总结(四)

    以下出现代码均为伪代码;

    2.5.1 生产者-消费者问题(考试模型占比 60%)


    分析上图:缓冲区存在互斥问题,生产者与消费者之间还存在同步问题

    1. 利用记录型信号量解决生产者-消费者问题(背下来)

      假定在生产者和消费者之间的共用缓冲池中具有 n 个缓冲区,这时可以利用互斥信号量 mutex(初始值 1 )实现进程对缓冲池的互斥作用;利用信号量 empty(初始值为n)和 full(初始值为0)分别表示缓冲池中空缓冲区和满缓冲区的数量。
      又假定这些生产者和消费者相互等效,只要总缓冲区未满,生产者便可将消息送入缓冲池中,只要缓冲池没空,消费者便可从缓冲池中取走一个消息

    semaphore mutex =1; //缓冲区互斥操作的互斥信号量
    semaphore empty = n; //空缓冲区数目,类型为资源信号量
    semaphore full = 0; //满缓冲区为数目,类型为资源信号量
    item buffer[n] 
    int in =0,out =0;
    producer() //生产者进程
    {
          while(1)
          {
                produce an item nextp;
                P(empty); //empty - 1,资源信号量又有同步作用
                P(mutex); //加锁————>当多个生产者的时候要互斥访问
                buffer[in] = nextp;
                in = (in+1)%n; //循环队列
                V(mutex); //解锁
                V(full);  //full + 1
          }
    }
    consumer() //消费者进程
    {
          while(1)
          {
                P(full); //full - 1
                P(mutex); //加锁————>当多个消费者的时候要互斥访问
                nextc = buffer[out];
                out = (out+1)%n; //循环队列
                V(mutex); //解锁
                V(empty);  //full + 1
                consumer the item in nextc;
          }
    }
    

    注:
    (1)empty + full = n ;
    (2)若多个 P 操作颠倒,可能会存在死锁问题。


    2.利用AND信号量解决生产者-消费者问题 (了解就行)

    对于生产者-消费者问题,也可以利用AND信号量来解决,即:

    • 用 Swait(empty,mutex) 来代替 wait(empty) 和 wait(mutex)
    • 用 Ssignal(mutex,full) 来代替 signal(mutex) 和 signal(full)
    • 用 Swait(full,mutex) 来代替 wait(full) 和 wait(mutex)
    • 用 Ssignal(mutex,empty) 来代替 signal(mutex) 和 signal(empty)
    semaphore mutex =1; //缓冲区互斥操作的互斥信号量
    semaphore empty = n; //空缓冲区数目,类型为资源信号量
    semaphore full = 0; //满缓冲区为数目,类型为资源信号量
    item buffer[n] 
    int in =0,out =0;
    producer() //生产者进程
    {
          while(1)
          {
                produce an item nextp;
                Swait(empty,mutex)
                buffer[in] = nextp;
                in = (in+1)%n; //循环队列
                Ssignal(empty,mutex); //解锁
          }
    }
    consumer() //消费者进程
    {
          while(1)
          {
                Swait(full,mutex)
                nextc = buffer[out];
                out = (out+1)%n; //循环队列
                Ssignal(empty,mutex)
                consumer the item in nextc;
          }
    }
    

    3. 利用管程来解决生产者-消费者问题(自主命题

    建立一个管程,并命名为producerconsumer,或简称PC。其中包括两个过程:
    (1)put(x)过程。生产者利用该过程将自己生产的产品放到缓冲池中,并用整型变量count来表示在缓冲池中已有的产品数目,当count >= n 时,表示缓冲池已满,生产者等待。(也就意味需要有2个条件变量
    (2)get(x)过程。消费者利用该过程从缓冲池中取出一个产品,当count <= 0 时,表示缓冲池中已经无可取用的产品,消费者等待


    对于条件变量notfull 和 notempty,分别有两个过程cwait 和 csignal对它们进行操作:
    (1)cwait(condition)过程:当管程被一个进程占用时,其他进程调用该过程时阻塞,并挂在条件condition 的队列上
    (2)csignal(condition)过程:唤醒在cwait执行后阻塞在条件 condition 队列上的进程,如果这样的进程不止一个,则选择其中一个实施唤醒操作;如果队列为空,则无操作而返回

    monitor producer-consumer //类似于一个类
    {
          int in=0,out=0,count=0;
          itm buffer[n];
          condition notfull,notempty;
          void put(item nextp)
          {
                if count >= n then 
                      cwait (notfull)
                buffer [in]=nextp;
                in= (in+1)%n;
                count =count +1;
                csignal(notempty);
          }
          void get(item *x)
          {
                if count <= 0 then 
                      cwait (notempty)
                *x=buffer[out];
                out= (out+1)%n;
                count =count - 1;
                csignal(notfull);  //告诉生产者你可以生产了
          }
    }PC;
    //利用管程解决生产者-消费者问题
    process producer()
    {
          item x;
          while (TRUE)
          {
                produce an item in x;
                PC.put(item);  //相当于调用方法
          }
    }
    process consumer()
    {
          item x;
          while (TRUE)
          {
                PC.get(item);
                consumer an item in x;
          }
    }
    

    2.5.2 哲学家进餐问题(考试模型占比 10%)

      5个哲学家坐在桌子边,桌上有 5 个碗和 5 只筷子,哲学家的生活方式是交替的进行思考和进餐,哲学家饥饿时便拿起两边的筷子进餐,但只有当拿到两只筷子后才能进餐,用餐完毕放下筷子,继续思考。
    小提取:

    • 思考问题
    • 饿了停止思考,左手拿一只筷子(如果左侧哲学家已经持有它,需要等待)
    • 右手拿一只筷子(如果右侧哲学家已经持有它,需要等待)
    • 进餐
    • 放右手筷子
    • 放左手筷子
    • 重新回到思考问题状态


    1. 利用记录型信号量解决哲学家进餐问题(没有你先我后的同步问题)

    筷子对应临界资源,在一段时间内只允许一位哲学家使用。为实现筷子的互斥使用,可以用一个信号量表示一只筷子,由这五个信号量构成信号量数组。————> semaphore chopstick[5];

    方法一:互斥吃饭

      至多只允许四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并且吃完后,释放两只筷子

    semaphore chopstick[5]={1,1,1,1,1};
    semaphore eating = 4; //仅允许四个哲学家可以进餐
    void philosopher(int i)  // 第 i 位哲学家的活动可以描述为
    {
          while( 1 )
          {
                P(eating); //请求就餐,若是第五个则先挨饿
                P(chopstick[i]); //画个圈你就知道,这是他左手边的筷子
                P(chopstick[i+1] % 5);
                eating();  //进餐
                V(chopstick[i+1] % 5);  //释放右边筷子
                V(chopstick[i]); //释放左边
                V(eating);  //释放信号量给其他哲学家
          }
    }
    

    方法二:互斥拿筷子的动作

      仅当哲学家的左、右两只筷子均可用的时候,才可以进餐

    semaphore chopstick[5]={1,1,1,1,1};
    semaphore mutex = 1; //设置取筷子的信号量
    void philosopher(int i)  // 第 i 位哲学家的活动可以描述为
    {
          while( 1 )
          {
                thinking();
                P(mutex); //在取筷子前获得互斥量
                P(chopstick[i]); //画个圈你就知道,这是他左手边的筷子
                P(chopstick[i+1] % 5);
                V(mutex);  //释放互斥量
                eating();  //进餐
                V(chopstick[i+1] % 5);  //释放右边筷子
                V(chopstick[i]); //释放左边
          }
    }
    

    方法三:互斥拿筷子顺序(奇偶数)

      规定奇数号(1,3,5)哲学家先拿起左边筷子,然后再去拿右边筷子,而偶数(2,4)哲学家则相反,即五位哲学家先竞争奇数筷子,获得后再去竞争偶数筷子,最后总会由一位哲学家能过获得两只筷子而就餐,下图是哲学家抢奇数筷子图示。

    semaphore chopstick[5]={1,1,1,1,1};
    semaphore mutex = 1; //设置取筷子的信号量
    void philosopher(int i)  // 第 i 位哲学家的活动可以描述为
    {
          while( 1 )
          {
                thinking();
                if (i%2==0) //偶数哲学家,先右后左
                {
                      P(chopstick[i+1] % 5);
                      P(chopstick[i]);
                      eating();  //进餐
                      V(chopstick[i+1] % 5);  //释放右边筷子
                      V(chopstick[i]);
                }
                else 
                {
                      P(chopstick[i]);
                      P(chopstick[i+1] % 5);
                      eating();  //进餐
                      V(chopstick[i]);
                      V(chopstick[i+1] % 5);  //释放右边筷子
                }
          }
    }
    

    2. 利用 AND 信号量机制解决哲学家进餐问题

      要求每个哲学家先获得两个临界资源(筷子)后方能进餐,本质就是 AND 同步问题,故用 AND 信号量机制解决哲学家进餐问题是最简洁的解法

    semaphore chopstick[5]={1,1,1,1,1};
    process(i)
    {
          while(1)
          {
                think;
                Swait(chopstick[i+1] % 5,chopstick[i]); //把两个筷子全拿起来
                eat;
                Ssignal(chopstick[i+1] % 5,chopstick[i]);      
          }
    }
    

    2.5.3 读者 - 写者问题(考试模型占比 30%)

    注:读者变形(自主命题

    • 公平队列
    • 写者优先问题

      一个数据文件或记录可被多个进程共享,只要求读该文件的进程称为 “Reader 进程”,其他进程为 “Writer 进程”,允许多个进程同时读一个共享对象,因为读不会使数据文件混乱,不允许一个 Writer 进程和其他Reader 进程或 Writer 进程同时访问一个对象。
      读者 - 写者问题:是保证一个 Writer 进程必须与其他进程互斥访问共享对象的同步问题。


    1. 利用记录型信号量解决 读者 - 写者 问题(读者优先)

    &emsp: 为实现 Reader 和 Writer 进程间在读或写时的互斥而设置了一个互斥信号量 Wmutex.设置一个整型变量 Readcount 表示正在读的进程数目,由于只要一个 Reader 进程在读,便不允许 Writer 进程去写

    semaphore rmutex = 1,wmutex = 1; //readcount 和文件的互斥信号量
    int readcount=0;
    Reader()  //这种方式用于——>独木桥问题
    {
          while(1)
          {
                P(rmutex); //允许多个进程同时读一个共享对象
                if (readcount == 0)
                      P(wmutex); //缓冲区互斥
                readcount ++;
                V(rmutex);
                perform read operation;
                P(rmutex);
                readcount --;
                if (readcount == 0)
                      V(wmutex);
                V(rmutex);
          }      
    }
    Writer()
    {
          while(1)
          {
                P(wmutex);
                perform write operation;
                V(wmutex);
          }
    }
    

    说个简单问题, 读者 - 写者 问题相当于一个班用自习室抢房间,第一个人来了就开始占房间使用,陆续班上同学进入教室自习,当自习结束且直到最后一个人走了,这个房间不再使用


    2. 利用信号量集机制解决 读者 - 写者

      增加一个限制,最多只允许 RN 个读者同时都。为此引入一个信号量L,并赋予初值为RN,通过执行 wait(L,1,1) 操作来控制读者数目。每当有一个读者进入时,就要先执行 wait(L,1,1) 操作,使L的值减 1 。当有 RN 个读者进入后,L 便减为 0 ,第 RN+1 个读者要进入读时,必然因为 wait(L,1,1)操作失败而堵塞。

    int RN = N;
    semaphore L = RN,mx = 1;
    void Rreder() 
    {
          while (TRUE)
          {
                Swait(L,1,1);
                Swait(mx,1,0);  //1,1,0时没有操作,开关;
                perform read operation;
                Ssignal(L,1);
          }
    }
    void Writer() 
    {
          while (TRUE)
          {
                Swait(mx,1,1;L,RN,0);
                perform writer operation;
                Ssignal(mx,1);
          }
    }
    
    哪有什么胜利可言,坚持意味着一切
  • 相关阅读:
    BZOJ 1911: [Apio2010]特别行动队 斜率优化dp
    BZOJ 2751: [HAOI2012]容易题(easy) 数学
    Wunder Fund Round 2016 (Div. 1 + Div. 2 combined) B. Guess the Permutation 水题
    Wunder Fund Round 2016 (Div. 1 + Div. 2 combined) A. Slime Combining 水题
    BZOJ 2768: [JLOI2010]冠军调查 最小割
    BZOJ 1497: [NOI2006]最大获利 最小割
    Codeforces Round #140 (Div. 1) D. The table 构造
    ICPC-CAMP day1 D.Around the world
    Codeforces Round #340 (Div. 2) E. XOR and Favorite Number 莫队算法
    BZOJ 2038 [2009国家集训队]小Z的袜子 莫队
  • 原文地址:https://www.cnblogs.com/BY1314/p/12842134.html
Copyright © 2011-2022 走看看