多线程模拟冰淇淋店售货问题

来源:互联网 发布:门锁软件注册码生成器 编辑:程序博客网 时间:2024/04/28 04:21

原创出自:http://blog.csdn.net/wudaijun/article/details/8236291


多线程同步看懂了这个问题应该就差不多了。

问题描述:
     在一个冰淇淋店中 主要有老板,店员,收银员,以及顾客四个角色 这里用多线程来模拟他们之间的交互关系,并用伪代码描述
     主要流程:
          10个顾客到冰淇淋店买冰淇淋,假设每个顾客需要买1到4个冰淇淋,对于新需求的每个冰淇淋,该店都会新分配一个店员来制作,也就是店里此时一共有10到40个店员制作冰淇淋。当店员将冰淇淋制作完成后,先到老板办公室交给老板审核,如果审核不通过,则重做,审核通过再交给顾客,顾客拿到冰淇淋后到收银员处排队结帐。

     交互约束:
          1.老板办公室最多只能一个店员进入,不可同时进入两个店员
          2.老板对顾客需求的所有冰淇淋审核完成后才能离开
          2.顾客拿到冰淇淋到收银员那去排队交钱时,必须满足FIFO性质

     分析及实现:
          主函数:创建10个顾客线程 并且模拟产生冰淇淋总需求个数,用于创建店员和老板审核
                  这里并没有创建店员线程 因为店员线程虽然一共有TotalNeeds个,可以在main中创建,但是放在顾客线程中更合逻辑,因为店员是为了满足顾客需求而产生并工作的
          #define NUM_CUSTOM 10

          void main()
          {
               int TotalNeeds = 0;
               for(int i=0; i<NUM_CUSTOM; i++)
               {
                    int num = Random(1...4);
                    ThreadNew(...Customer, 1, num);//创建顾客线程并传入其模拟购买的冰淇淋数
                    TotalNeeds += num; 
               }
               ThreadNew(...Manager, 1, TotalNeeds);//创建老板线程传入总需求冰淇淋数
               ThreadNew(...Cashier, 0);            //创建收银员线程,无参数
               RunAllThread();                      //运行所有线程
          }

          这里面涉及到很多的同步和互斥问题,先从最简单的老板开始分析 老板线程实现如下
          以下是老板和店员交互所用到的数据集
          struct
          {
               bool       passed;   (false)//用于店员得到老板审核结果
               Semosphere requested;    (0)//用于老板等待店员请求
               Semosphere finished;     (0)//用于店员等待老板审核完成
               Semopshere locked;       (1)//用于实现店员对老板办公室的访问互斥
          }inspect;
          void Manager(int TotalNeeds)
          {
               int numChecked = 0;            //记录老板核查的总数
               int numCheckedAndPassed = 0;   //记录核查通过的总数
               while(numCheckedAndPassed < TotalNeeds)     //当合格数未到总需求,继续核查
               {
                    SemosphereWait(inspect.requested);     //等待核查请求
                    numChecked++;                          //核查数加1
                    inspect.passed = Random(true, false);  //模拟核查结果
                    if(inspect.passed)                     //如果合格,记录
                         numCheckedAndPassed++;
                    SemosphereSignal(inspect.finished);    //完成核查
               }
          }
          接下来是店员线程 店员线程需要和老板以及顾客打交道 和老板的交互就是审查 在审查通过后 他还需要通知顾客
这里通知顾客需要注意  由于若干顾客线程在同时运行 因此店员在通过审查后 需要发出一个只有对应顾客才能辨识的信号量 这可以通过在顾客线程中设置局部变量传给店员或者是在全局中声明一个信号量数组来实现 这里采用前一种
          void Clerk(Semosphere clerkDone)
          {
               bool passed = false;
               while(!passed)                                //如果没有通过,就一直做
               {
                    MakeIceCream();                          //制作过程
                    SemosphereWait(inspect.locked);          //等待获得老板办公室权限
                    SemosphereSignal(inspect.requested);     //给老板发送请求
                    SemosphereWait(inspect.finished);        //等待老板核查
                    passed = inspect.passed;                 //获取检验结果
                    SemosphereSignal(inspect.locked);        //出办公室要关门
               }
               SemosphereSignal(clerkDone);                   //将冰淇淋交给顾客
          }

          然后分析顾客线程  顾客线程主要和店员以及收银员交互 和收银员的交互 只需要创建其线程后 等待即可。
          而对于顾客和收银员的交互则比较复杂 首先主要是要涉及到排队的FIFO问题 如何在多线程中实现。
这其实是一个一对多的同步问题,只是这里需要特殊处理FIFO序列。首先各个顾客在排队时应该有一个号码牌 这个号码牌可以让收银员在处理完你的帐单后通过号码明确的找到你。并且这个号码最好刚好标识了顾客排队的顺序 以便于收银员顺序地等待并且处理顾客请求,即这个同步问题的应答是通过多个信号量来标识的。 顾客在排队时先获取一个号码 然后发出请求 等待接收该号码的处理完成标识。那么这里的请求是否也需要多个信号量呢? 实际上,前面已经提到,我们按照一定的顺序来对用户排队序列进行标识,这样使得在收银员线程中只需要按照队列好 0 1 2 3..顺序处理并且发出处理完成信号即可,因为顾客是按照这个编号请求的
          如果不按照此顺序的话会更加麻烦,因为同步必定需要两个信号量,如果请求和应答均按照信号量数组的方式的话,那么收银员需要在收到请求时,得到客户放在某结构体中的本身编号,然后再释放其本身编号所对应的应答信号量。
          根据上面提到的所需数据 定义队列结构体 用于收银员和顾客通信
          struct
          {
               int number; (0);
               Semosphere numLock (1);
               Semosphere requested (0);
               Semosphere finished[NUM_CUSTOM] (0);
          }line;
                                           
          void Customer(int numIceCream)
          {
               Semosphere clerkDone;     (0)                 //建立只有自己才能够识别的信号量 
               for(int i=0; i<numIceCream; i++)              //创建店员线程
                    CreateThread(...,Clerk, 1, clerkDone);    

               for(i=0; i<numIceCream; i++)                   //等待制作完成
                    SemosphereWait(clerkDone);

               WalkToCashier();                                //准备结帐
               SemosphereWait(line.numLock);                   //进入临界区,获得排列号
               int place = line.number++;
               SemosphereSignal(line.numLock);
               SemosphereSignal(line.requested);               //发送请求(这里的requested可以累加)
               SemosphereWait(line.finished[place]);           //等待处理完成
       }

       最后是收银员线程
       void cashier()
       {
          for(int i=0; i<NUM_CUSTOM; i++)                      //依次处理
          {
               SemosphereWait(line.requested);                 //等待请求
               CashOut(i);                                     //收钱
               SemosphereSignal(line.finished[i]);             //通知对应顾客
          }
          
       }

原创粉丝点击