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<string> colaTareas =
new Queue<string> ();
// 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 ();
}
}
}