La motivación
comenzó al entrar en contacto con el modelo de programación multiproceso (proceso), y lo que aprendí fueron las primitivas de sincronización relacionadas con el semáforo (Semaphore). No sé por qué no hay elementos correspondientes en .Net Framework. Lo terrible es que la usé para implementar muchos códigos C++ probados en el tiempo. Para evitar que los mártires revolucionarios tomaran la medicina y sangraran en vano, tuve que dar a luz a uno yo mismo.
¿Qué es un semáforo?
Si ya comprende el concepto de semáforo, omita esta sección.
Semaphore es una instalación utilizada en un entorno de subprocesos múltiples. Es responsable de coordinar varios subprocesos para garantizar que puedan utilizar los recursos públicos de forma correcta y razonable.
Echemos un vistazo a cómo funciona un estacionamiento. En aras de la simplicidad, supongamos que el estacionamiento tiene solo tres espacios de estacionamiento y que los tres espacios de estacionamiento están vacíos al principio. Esto es, si cinco autos vienen al mismo tiempo, y el portero permite que tres de ellos entren sin obstáculos, y luego coloca el bloqueo de autos, los autos restantes deberán esperar en la entrada, y los autos siguientes también tendrán que esperar en la entrada. entrada. En ese momento, un automóvil salió del estacionamiento. Después de que el portero se enteró, abrió la barrera para automóviles y metió un automóvil. Si salían dos autos más, podía poner dos autos más, y así sucesivamente.
En este sistema de estacionamiento, los espacios de estacionamiento son recursos públicos, cada automóvil es como un hilo y el portero actúa como un semáforo.
Además, las características del semáforo son las siguientes: el semáforo es un número entero no negativo (el número de estacionamientos), y todos los subprocesos (vehículos) que lo pasan reducirán el número entero en uno (pasarlo es, por supuesto, usar recursos). ). Cuando el valor entero es cero, todos los subprocesos que intenten pasarlo estarán en estado de espera. Definimos dos operaciones sobre el semáforo: Esperar y Soltar. Cuando un hilo llama a la operación Esperar, pasa y luego disminuye el semáforo en uno, o espera hasta que el semáforo sea mayor que uno o se agote el tiempo de espera. La liberación en realidad realiza una operación de adición en el semáforo, que corresponde a que el vehículo sale del estacionamiento. La razón por la que esta operación se llama "liberación" es que la operación de adición en realidad libera los recursos protegidos por el semáforo.
lograr
Como todos sabemos, las funciones de sincronización de subprocesos proporcionadas en la biblioteca de clases .Net Framework incluyen:
Monitor, AutoResetEvent, ManualResetEvent, Mutex, ReadWriteLock e InterLock. Entre ellos, AutoResetEvent, ManualResetEvent y Mutex se derivan de WaitHandler y, de hecho, encapsulan los objetos del kernel proporcionados por el sistema operativo. Otros deberían ser nativos de la máquina virtual .Net. Obviamente, las instalaciones de los objetos del kernel del sistema operativo son menos eficientes de usar. Sin embargo, la eficiencia no es un tema que debamos considerar aquí. Usaremos dos monitores y un objeto ManualResetEvent para simular un semáforo.
El código es el siguiente:
Semáforo de clase pública
{
privado ManualResetEvent waitEvent = nuevo ManualResetEvent (falso);
objeto privado syncObjWait = nuevo objeto();
private int maxCount = 1; //Número máximo de recursos
private int currentCount = 0; //El número actual de recursos
public Semaphore();
{
}
semáforo público (int maxCount)
{
this.maxCount = maxCount;
}
public bool Esperar()
{
lock( syncObjWait ) //Solo un hilo puede ingresar el siguiente código
{
bool waitResult = this.waitEvent.WaitOne(); // La cantidad de recursos esperando aquí es mayor que cero.
si(esperarResultado)
{
bloquear (esto)
{
si (cuentaactual> 0)
{
cuentaactual--;
si (cuentaactual == 0)
{
this.waitEvent.Reset();
}
}
demás
{
System.Diagnostics.Debug.Assert (falso, "El semáforo no permite el recuento actual <0");
}
}
}
devolver resultado de espera;
}
}
/**//// <resumen>
/// Operación de espera que permite el retorno del tiempo de espera
/// </summary>
/// <param nombre="milisegundosTimeout"></param>
/// <devoluciones></devoluciones>
bool público Esperar (int milisegundosTimeout)
{
lock( syncObjWait ) // El monitor garantiza que el código de clase de alcance esté dentro de la sección crítica
{
bool waitResult = this.waitEvent.WaitOne(milisegundosTimeout,false);
si(esperarResultado)
{
bloquear (esto)
{
si (cuentaactual> 0)
{
cuentaactual--;
si (cuentaactual == 0)
{
this.waitEvent.Reset();
}
}
demás
{
System.Diagnostics.Debug.Assert (falso, "El semáforo no permite el recuento actual <0");
}
}
}
devolver resultado de espera;
}
}
Lanzamiento de bool público()
{
lock( this ) // El monitor garantiza que el código de clase de alcance esté dentro de la sección crítica
{
cuentaactual++;
si (cuentaactual> this.maxCount)
{
currentCount = this.maxCount;
devolver falso;
}
this.waitEvent.Set(); //Permitir que el hilo llame a Esperar para ingresar
}
devolver verdadero;
}
}