打印机做两件事情:
第一件事件负责接受外界打印的请求,包括其他的电脑,把这个打印任务添加到打印队列当中。
另一件事情就是打印,从打印队列中取出一个打印任务,完成打印任务,将这个打印任务去掉。
可以肯定的是,这两件事情是并发进行的,不可能打印机一直去打印,而不去接受新的打印任务,也不可能一直接受请求,而不去打印,等到让油墨干了纸张烂掉了
如果我们用程序让打印机干活的话,显然我们可以用两个线程同时做这两件事情,当然还要考虑其他的事情,就是前面说的并发问题,因为存在着并发竞争资源--打印机队列。
我们希望的理想情况是最好两个线程能交替执行,即接受一次打印请求的操作,再执行一次打印的操作,而不是接受请求N次后才执行一次打印任务,所以说我们还需要解决线程之间配合工作的问题,也就是线程协作的问题。
关于线程协作,我们考虑三个问题:
(1)如何在当前线程中通知其他的线程的执行
(2)如何阻止当前线程的执行
(3)其他线程执行完毕如何继续当前线程的执行
答案:(1)Monitor.Pulse()
(2)Monitor.Wait()
(3)Monitor.Wait()
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(); } }
发现:先把所有的生产任务添加进来,然后再执行消费作业。这是不符合我们的要求的。我们要求是添加一个打印任务就执行一次消费作业
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线程从阻塞队列进入准备队列,然后他判断while(Monitor.Wait(queue); ),因为这是第一次执行,所以
结果显示是 生产者生产一个,消费者接着就消费一个。如此循环中。。。
人一定要靠自己Copyright © 广州京杭网络科技有限公司 2005-2025 版权所有 粤ICP备16019765号
广州京杭网络科技有限公司 版权所有