打印机做两件事情:
第一件事件负责接受外界打印的请求,包括其他的电脑,把这个打印任务添加到打印队列当中。
另一件事情就是打印,从打印队列中取出一个打印任务,完成打印任务,将这个打印任务去掉。
可以肯定的是,这两件事情是并发进行的,不可能打印机一直去打印,而不去接受新的打印任务,也不可能一直接受请求,而不去打印,等到让油墨干了纸张烂掉了
如果我们用程序让打印机干活的话,显然我们可以用两个线程同时做这两件事情,当然还要考虑其他的事情,就是前面说的并发问题,因为存在着并发竞争资源--打印机队列。
我们希望的理想情况是最好两个线程能交替执行,即接受一次打印请求的操作,再执行一次打印的操作,而不是接受请求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); ),因为这是第一次执行,所以
结果显示是 生产者生产一个,消费者接着就消费一个。如此循环中。。。
如对本文有疑问, 点击进行留言回复!!
使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)
C#实现获取本地内网(局域网)和外网(公网)IP地址的方法分析
网友评论