1. Instance background
Printers do two things:
The first event is responsible for accepting requests for external printing, including other computers, to add this printing task to the print queue.
Another thing is to print, take a print job out of the print queue, finish the print job, and remove the print job.
You can be sure that these two things are happening in parallel. It is impossible for the printer to print all the time without accepting a new print job or to keep accepting requests without printing until the ink has dried up and the paper has rotted away
If we use a program to get the printer to work, it's clear that we can do both things with two threads at the same time. Of course, there are other things to consider, that is, the concurrency problem mentioned earlier, because there is a concurrent competing resource--the printer queue.
Ideally, we want the best two threads to execute alternately, that is, to accept a print request and perform a print operation again, instead of accepting a request N times before executing a print task, so we also need to solve the problem of working together between threads, that is, the problem of thread collaboration.
There are three questions to consider about thread collaboration:
(1) How to notify other threads of execution in the current thread
(2) How to prevent the current thread from executing
(3) How to continue the execution of the current thread when the execution of other threads is complete
Answer: (1) Monitor.Pulse()
(2)Monitor.Wait()
(3)Monitor.Wait()
2. Printer work without thread collaboration
class MonitorTest { int MAX = 10;//Allow up to 10 print jobs Queue<int> queue; //Represents a FIFO collection of objects public MonitorTest() { queue = new Queue<int>(); } //Method invoked by the producer thread: Simulate the addition of a print job public void ProducerThread() { Random r = new Random(); lock (queue) { for (int counter = 0; counter < MAX; counter++) { int value = r.Next(100); queue.Enqueue(value); //Random Numbers Queued Console.WriteLine("Production:" + value); } } } //Consumer Threads public void ConsumerThread() { lock (queue) { for (int counter = 0; counter < MAX; counter++) { int value = (int)queue.Dequeue(); //First element out of queue Console.WriteLine("Consumption:" + 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("The printer is finished"); Console.ReadLine(); } }
Discovery: Add all production tasks first, and then execute consumer jobs.This does not meet our requirements.We want to do a consumer job by adding a print job
3. Printer Work with Thread Collaboration
class MonitorTest { int MAX = 10;//Allow up to 10 print jobs Queue<int> queue; //Represents a FIFO collection of objects public MonitorTest() { queue = new Queue<int>(); } //Method invoked by the producer thread: Simulate the addition of a print job public void ProducerThread() { Random r = new Random(); lock (queue) { for (int counter = 0; counter < MAX; counter++) { int value = r.Next(100); queue.Enqueue(value); //Random Numbers Queued Console.WriteLine("Production:" + value); //producer Thread Notification consumer Thread from blocked queue to ready queue Monitor.Pulse(queue); //Release Waiting Threads //producer The thread enters the blocked queue and discards the lock so that consumer Threads executed Monitor.Wait(queue); //wait for CosumerThread()complete } } } //Consumer Threads public void ConsumerThread() { lock (queue) { do { if (queue.Count>0) { int value = (int)queue.Dequeue(); //First element out of queue Console.WriteLine("Consumption:" + value); Monitor.Pulse(queue);//release } } while (Monitor.Wait(queue)); // wait for ProducerThread()Put data in } } 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("The printer is finished"); Console.ReadLine(); } }
Explanation:
When the producer thread Start starts and enters the running state, a random number is generated and added to the queue queue container, then the code Monitor.Pulse(queue) is encountered; then the producer thread notifies the thread consumer to enter the preparation queue from the blocked queue.The producer thread then encounters Monitor.Wait(queue); the producer thread enters a waiting state (that is, it blocks itself) and discards the lock on the queue, so the thread consumer executes.
Then the thread consumer executes, and when it reaches the do**while loop, it "pulls out" the first element from the queue without judging the condition because of the first execution, and then it encounters the code Monitor.Pulse(queue); then the thread consumer notifies the producer thread to enter the preparation queue from the blocked queue, andLater he judged while (Monitor.Wait(queue);), because this is the first execution, so
The result shows that the producer produces one, then the consumer consumes one.In such a cycle.