zoukankan      html  css  js  c++  java
  • 5、线程的协作问题

    1、实例背景

      打印机做两件事情:

            第一件事件负责接受外界打印的请求,包括其他的电脑,把这个打印任务添加到打印队列当中。

            另一件事情就是打印,从打印队列中取出一个打印任务,完成打印任务,将这个打印任务去掉。

         可以肯定的是,这两件事情是并发进行的,不可能打印机一直去打印,而不去接受新的打印任务,也不可能一直接受请求,而不去打印,等到让油墨干了纸张烂掉了

    如果我们用程序让打印机干活的话,显然我们可以用两个线程同时做这两件事情,当然还要考虑其他的事情,就是前面说的并发问题,因为存在着并发竞争资源--打印机队列。

    我们希望的理想情况是最好两个线程能交替执行,即接受一次打印请求的操作,再执行一次打印的操作,而不是接受请求N次后才执行一次打印任务,所以说我们还需要解决线程之间配合工作的问题,也就是线程协作的问题。

    关于线程协作,我们考虑三个问题:

    (1)如何在当前线程中通知其他的线程的执行

    (2)如何阻止当前线程的执行

    (3)其他线程执行完毕如何继续当前线程的执行

       答案:(1Monitor.Pulse()

             (2)Monitor.Wait()

             (3)Monitor.Wait()

    2没有线程协作的打印机工作

    class MonitorTest
    
        {
    
            int MAX = 10;//最多允许10个打印作业
    
     
    
            Queue<int> queue; //表示对象的先进先出的集合
    
            public MonitorTest()
    
            {
    
                queue = new Queue<int>();
    
            }
    
     
    
            //生产者线程调用的方法:模拟添加打印作业
    
            public void ProducerThread()
    
            {
    
                Random r = new Random();
    
     
    
                lock (queue)
    
                {
    
                    for (int counter = 0; counter < MAX; counter++)
    
                    {
    
                        int value = r.Next(100);
    
                        queue.Enqueue(value);    //随机数入队列
    
                        Console.WriteLine("生产:" + value);
    
                    }
    
                }
    
            }
    
     
    
            //消费者线程
    
            public void ConsumerThread()
    
            {
    
                lock (queue)
    
                {
    
                    for (int counter = 0; counter < MAX; counter++)
    
                    {
    
                        int value = (int)queue.Dequeue(); //第一个元素出队列
    
                        Console.WriteLine("消费:" + value);
    
                    }
    
                }
    
            }
    
     
    
            static void Main(string[] args)
    
            {
    
                MonitorTest monitor = new MonitorTest();
    
                Thread producer = new Thread(new ThreadStart(monitor.ProducerThread));
    
                Thread consumer = new Thread(new ThreadStart(monitor.ConsumerThread));
    
     
    
                producer.Start();
    
                consumer.Start();
    
     
    
                Console.WriteLine("打印机工作完毕");
    
                Console.ReadLine();
    
            }
    
        }

     

    发现:先把所有的生产任务添加进来,然后再执行消费作业。这是不符合我们的要求的。我们要求是添加一个打印任务就执行一次消费作业

    3有线程协作的打印机工作

      

    class MonitorTest
    
        {
    
            int MAX = 10;//最多允许10个打印作业
    
     
    
            Queue<int> queue; //表示对象的先进先出的集合
    
            public MonitorTest()
    
            {
    
                queue = new Queue<int>();
    
            }
    
     
    
            //生产者线程调用的方法:模拟添加打印作业
    
            public void ProducerThread()
    
            {
    
                Random r = new Random();
    
     
    
                lock (queue)
    
                {
    
                    for (int counter = 0; counter < MAX; counter++)
    
                    {
    
                        int value = r.Next(100);
    
                        queue.Enqueue(value);    //随机数入队列
    
                        Console.WriteLine("生产:" + value);
    
     
    
                        //producer线程通知consumer线程从阻塞队列进入准备队列
    
                        Monitor.Pulse(queue); //释放等待线程
    
                        //producer线程进入阻塞队列,并放弃了锁定,使consumer线程得以执行
    
                        Monitor.Wait(queue);  //等待CosumerThread()完成
    
     
    
                    }
    
                }
    
            }
    
     
    
            //消费者线程
    
            public void ConsumerThread()
    
            {
    
                lock (queue)
    
                {
    
                    do
    
                    {
    
                        if (queue.Count>0)
    
                        {
    
                            int value = (int)queue.Dequeue(); //第一个元素出队列
    
                            Console.WriteLine("消费:" + value);
    
     
    
                            Monitor.Pulse(queue);//释放
    
                        }
    
                    } while (Monitor.Wait(queue)); // 等待ProducerThread()放入数据
    
                }
    
            }
    
     
    
            static void Main(string[] args)
    
            {
    
                MonitorTest monitor = new MonitorTest();
    
                Thread producer = new Thread(new ThreadStart(monitor.ProducerThread));
    
                Thread consumer = new Thread(new ThreadStart(monitor.ConsumerThread));
    
     
    
                producer.Start();
    
                consumer.Start();
    
     
    
                Console.WriteLine("打印机工作完毕");
    
                Console.ReadLine();
    
            }
    
        }
    
     

    解释:

    producer线程Start启动后,进入运行状态后,产生了一个随机数,并添加到queue队列容器里,然后碰到代码 Monitor.Pulse(queue) ; 那么producer线程就通知线程consumer从阻塞队列进入准备队列。然后producer线程又碰到Monitor.Wait(queue); producer线程进入等待状态(即阻塞了自己),他放弃了对queue的锁定,所以线程consumer得以执行。

      那么线程consumer执行了,当执行到do**while循环处,由于第一次执行,所以不判断条件,直接从queue队列里“请出”第一个元素,然后他碰到代码Monitor.Pulse(queue) ; ,那么线程consumer就通知producer线程从阻塞队列进入准备队列,然后他判断whileMonitor.Wait(queue); ),因为这是第一次执行,所以

     

    结果显示是 生产者生产一个,消费者接着就消费一个。如此循环中。。。

  • 相关阅读:
    动态获取页面参数内容
    服务器处理静态文件请求
    最简单的Web服务器
    控制台浏览器代码实战
    4.caffe资源汇总(更新中)
    3. caffe中 python Notebook
    2.caffe初解
    1.caffe初入
    有监督学习和无监督学习
    MySQL 之基础操作及增删改查等
  • 原文地址:https://www.cnblogs.com/schangxiang/p/11297098.html
Copyright © 2011-2022 走看看