using System; using System.Threading; using System.Collections.Generic; namespace Recetas.CSharp.Cap04.R0407 { public sealed class SincronizacionConsola { // Objeto bloqueador para sincronización: private static object lockerConsola = new object(); // Cola para las tareas a ejecutar: private static Queue colaTareas = new Queue (); // Valor bandera para indicar la continuación o // fin del procesamiento de tares: private static bool procesarMas = true; // Método utilitario para mostrar información del // thread en ejecución: private static void MostrarInfoThread (string mensaje) { lock (lockerConsola) { Console.WriteLine ("[{0,3}/{1}] - {2} : {3}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread ? "pool" : "primer plano", DateTime.Now.ToString ("HH:mm:ss.ffff"), mensaje ); } } // Método que ejecutará cada thread para procesar los elementos // en la cola `colaTareas`: private static void ProcesarTareasCola() { // Representa uno de los de la cola: string elementoCola = null; MostrarInfoThread ("El thread ha iniciado. Procesando elementos..."); // Mientras que valor bandera `procesarMas` sea verdadero, es decir // mientras que no se haya emitido la señal de terminación // continuar el procesamiento de los elementos de la cola `colaTareas`: while (procesarMas) { // Entra en región crítica: Monitor.Enter (colaTareas); try { // Extrae un elemento de la cola, y espera por el siguiente // (si hay uno disponible: if (colaTareas.Count == 0) { MostrarInfoThread ("No hay elementos en la cola, en espera..."); // Espera hasta que se invoce el método Pulse // con colaTareas: Monitor.Wait (colaTareas); } else { // Obtención del siguiente elemento de la cola: elementoCola = colaTareas.Dequeue(); } } finally { // Libera el bloque sobre la región crítica: Monitor.Exit (colaTareas); } // Procesa otro elemento de cola si existe: if (elementoCola != null) { // Bloque el acceso a la consola y muestra una serie de // mensajes: lock (lockerConsola) { for (int i = 0; i < 5; ++i) { MostrarInfoThread ("Procesando: " + elementoCola); Thread.Sleep (200); } } // Reestablece el valor por defecto de elementoCola: elementoCola = null; } } // Cuando ya no hay más elementos a procesar: MostrarInfoThread ("Finalizando..."); } public static void Main() { MostrarInfoThread ("Inicio de creación de threads..."); // Agregación de un elemento a la cola de tareas: lock (colaTareas) { colaTareas.Enqueue ("Tarea No. 1"); } // Crea e inicia 3 threads para la ejecución // del método ProcesarTareasCola: for (int i = 1; i <= 3; ++i) { (new Thread(ProcesarTareasCola)).Start(); } Thread.Sleep (1500); /// Cuando el usuario presione la tecla Enter, // se agrega una tarea y se activa un thread // para procesarla: MostrarInfoThread ("Presione Enter para activar un thread en espera."); Console.ReadLine (); // Agrega otro elemento a la cola de tareas: lock (colaTareas) { // Agrega una nueva tarea: colaTareas.Enqueue ("Tarea No. 2"); // Activa un thread en espera: Monitor.Pulse (colaTareas); } Thread.Sleep (2000); // Cuando el usuario presione Enter se agregan // 3 tareas a la cola, y se activan 3 threads // para procesarlas: MostrarInfoThread ("Presione Enter para activar 3 threas en espera."); Console.ReadLine (); // Agreaga otros tres elementos a la cola y // activa su procesamiento: lock (colaTareas) { colaTareas.Enqueue ("Tarea No. 3"); Monitor.Pulse (colaTareas); colaTareas.Enqueue ("Tarea No. 4"); Monitor.Pulse (colaTareas); colaTareas.Enqueue ("Tarea No. 5"); Monitor.Pulse (colaTareas); } Thread.Sleep (3500); // Cuando el usuario presione Enter, se emite una señal // para para terminar todas las tareas: MostrarInfoThread ("Presione Enter para procesar todas las tareas."); Console.ReadLine (); // Finaliza la cola de espera: lock (colaTareas) { // Emite la señal para la finalziación de los // threads: procesarMas = false; Monitor.PulseAll (colaTareas); } Thread.Sleep (1000); // Finalización: MostrarInfoThread ("El método Main finalizó. Presione Enter."); Console.ReadLine (); } } }