public Consumer(string id, PCQueue pcQueue, LocationList constituencyList, ProgressManager progManager) { this.id = id; finished = false; // Initially not finished this.pcQueue = pcQueue; this.constituencyList = constituencyList; this.progManager = progManager; (T = new Thread(run)).Start(); // Create a new thread for this consumer and get it started RunningThreads++; // Increment the number of running consumer threads; }
public void RunProducerConsumer() { //Create cyclist list to hold individual cyclist objects read from datasets locList = new LocationList(); // Create progress manager with number of files to process ProgressManager progManager = new ProgressManager(configData.configRecords.Count); // Output message to indicate that the program has started progressLbl.Text = "Creating and starting all producers and consumers"; // Create a PCQueue instance, give it a capacity of 4 var pcQueue = new PCQueue(4); // Create 2 Producer instances and 2 Consumer instances, these will begin executing on // their respective threads as soon as they are instantiated Producer[] producers = { new Producer("P1", pcQueue, configData, IOhandler), new Producer("P2", pcQueue, configData, IOhandler) }; Consumer[] consumers = { new Consumer("C1", pcQueue, locList, progManager), new Consumer("C2", pcQueue, locList, progManager) }; // Keep producing and consuming until all work items are completed while (progManager.ItemsRemaining > 0) { ; } // Output message to indicate that the program is shutting down progressLbl.Text = "Shutting down all producers and consumers"; // Deactivate the PCQueue so it does not prevent waiting producer and/or consumer threads // from completing pcQueue.Active = false; // Iterate through producers and signal them to finish foreach (var p in producers) { p.Finished = true; } // Iterate through consumers and signal them to finish foreach (var c in consumers) { c.Finished = true; } // We need to ensure that no thread waiting on Monitor.Wait() is stranded with // no Monitor.Pulse() now possible since all producer and consumer threads have // been signalled to stop, in the worse case all such threads could be stranded // so pulse that many times to ensure enough pulses are made available (or the // program can halt erroneously), wasted pulse are simply ignored for (int i = 0; i < (Producer.RunningThreads + Consumer.RunningThreads); i++) { lock (pcQueue) { // Pulse the PCQueue to signal any waiting threads Monitor.Pulse(pcQueue); // Give a short break to the main thread so the pulses have time to be // detected by any potentially waiting producer and/or consumer threads Thread.Sleep(100); } } // Once all producer and consumer threads have finally finished we can gracefully // shutdown the main thread, this is achieved by spinning on a while() loop until // there are no running threads, in this case we do not mind the main thread // spinning as we are about to shutdown the program while ((Producer.RunningThreads > 0) || (Consumer.RunningThreads > 0)) { ; // Wait by spinning } Console.WriteLine(); progressLbl.Text = "All producers and consumers shut down"; }