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); ),因为这是第一次执行,所以

 

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

原文地址:https://www.cnblogs.com/schangxiang/p/11297098.html