c# - squad - No entiendo la necesidad de Monitor.Pulse()
thread sync black squad (3)
Según MSDN , Monitor.Wait()
:
Libera el bloqueo en un objeto y bloquea el subproceso actual hasta que vuelva a adquirir el bloqueo.
Sin embargo, todo lo que he leído sobre Wait () y Pulse () parece indicar que simplemente liberar el bloqueo en otro hilo no es suficiente. Necesito llamar a Pulse () primero para activar el hilo en espera.
Mi pregunta es ¿por qué? Los hilos que esperan el bloqueo en un Monitor.Enter () solo lo consiguen cuando se libera. No hay necesidad de "despertarlos". Parece derrotar la utilidad de Wait ().
p.ej.
static object _lock = new Object();
static void Main()
{
new Thread(Count).Start();
Sleep(10);
lock (_lock)
{
Console.WriteLine("Main thread grabbed lock");
Monitor.Pulse(_lock) //Why is this required when we''re about to release the lock anyway?
}
}
static void Count()
{
lock (_lock)
{
int count = 0;
while(true)
{
Writeline("Count: " + count++);
//give other threads a chance every 10th iteration
if (count % 10 == 0)
Monitor.Wait(_lock);
}
}
}
Si uso Exit () y Enter () en lugar de Wait (), puedo hacer:
static object _lock = new Object();
static void Main()
{
new Thread(Count).Start();
Sleep(10);
lock (_lock) Console.WriteLine("Main thread grabbed lock");
}
static void Count()
{
lock (_lock)
{
int count = 0;
while(true)
{
Writeline("Count: " + count++);
//give other threads a chance every 10th iteration
if (count % 10 == 0)
{
Monitor.Exit(_lock);
Monitor.Enter(_lock);
}
}
}
}
Lea la sección de Comentarios de la página de MSDN vinculada:
Cuando un hilo llama a Wait, libera el bloqueo en el objeto y entra en la cola de espera del objeto. El siguiente hilo en la lista de espera del objeto (si hay uno) adquiere el bloqueo y tiene uso exclusivo del objeto. Todos los subprocesos que llaman Esperar permanecen en la cola de espera hasta que reciban una señal de Pulse o PulseAll , enviada por el propietario del bloqueo. Si se envía Pulse, solo se ve afectado el hilo en la cabecera de la cola de espera. Si se envía PulseAll, se verán afectados todos los subprocesos que esperan el objeto. Cuando se recibe la señal, uno o más subprocesos salen de la cola de espera e ingresan a la cola lista. Se permite un subproceso en la cola lista para volver a adquirir el bloqueo.
Este método se devuelve cuando el subproceso de llamada vuelve a adquirir el bloqueo en el objeto. Tenga en cuenta que este método se bloquea indefinidamente si el titular del bloqueo no llama a Pulse o PulseAll .
Entonces, básicamente, cuando llama a Monitor.Wait
, su hilo está en la cola de espera. Para que vuelva a adquirir el bloqueo, debe estar en la cola lista. Monitor.Pulse
mueve el primer hilo en la cola de espera a la cola lista y, por lo tanto, permite que vuelva a adquirir el bloqueo.
Utiliza Enter
/ Exit
para adquirir acceso exclusivo a un bloqueo.
Usas Wait
/ Pulse
para permitir la notificación cooperativa: quiero esperar a que ocurra algo, así que entro al bloqueo y llamo a Wait
; el código de notificación entrará en el bloqueo y llamará a Pulse
.
Los dos esquemas están relacionados, pero no están tratando de lograr lo mismo.
Considera cómo implementarías una cola de productor / consumidor donde el consumidor pueda decir "Despiértame cuando tengas un artículo para que lo consuma" sin algo como esto.
Yo mismo tuve la misma duda y, a pesar de algunas respuestas interesantes (algunas de ellas presentes aquí), seguí buscando una respuesta más convincente.
Creo que una idea interesante y simple sobre este tema sería: Puedo llamar a Monitor.Wait (lockObj) en un momento particular en el que ningún otro hilo está esperando para obtener un bloqueo en el objeto lockObj . Solo quiero esperar a que algo suceda (el estado de algún objeto cambia, por ejemplo), que es algo que sé que sucederá eventualmente, en algún otro hilo. Tan pronto como se logre esta condición, quiero poder volver a adquirir el bloqueo tan pronto como el otro hilo libere su bloqueo.
Por la definición del método Monitor.Wait , libera el bloqueo e intenta adquirirlo nuevamente. Si no esperó a que se llame al método Monitor.Pulse antes de intentar volver a adquirir el bloqueo, simplemente liberará el bloqueo y lo volverá a adquirir (dependiendo de su código, posiblemente en bucle).
Es decir, creo que es interesante tratar de comprender la necesidad del método Monitor.Pulse observando su utilidad en el funcionamiento del método Monitor.Wait .
Piense de esta manera: "No quiero liberar este bloqueo e inmediatamente trato de adquirirlo nuevamente, porque NO QUIERO que YO sea el siguiente hilo para adquirir este bloqueo. Y tampoco quiero permanecer en un bucle que contiene una llamada a Thread.Sleep comprobando alguna bandera o algo para saber cuándo se ha alcanzado la condición que estoy esperando para poder intentar recuperar el bloqueo. Solo quiero "hibernar" y despertarme automáticamente. tan pronto como alguien me dice que la condición que estoy esperando se ha logrado ".