2012-08-25 22 views
5

http://www.codeproject.com/Articles/28785/Thread-synchronization-Wait-and-Pulse-demystifiedMonitor.Pulse y Wait - Comportamiento inesperado

colas de espera:

La cola de listos es el conjunto de hilos que están a la espera de una cerradura especial . Los métodos Monitor.Wait presentan otra cola: la cola de espera . Esto es obligatorio ya que la espera de un Pulso es distinta de espera para adquirir un bloqueo. Al igual que la cola lista, la cola de espera es FIFO.

patrón Recomendado:

Estas colas pueden conducir a un comportamiento inesperado. Cuando se produce un pulso, se lanza el encabezado de la cola de espera y se agrega a la cola lista . Sin embargo, si hay otros subprocesos en la lista de espera, adquirirá el bloqueo antes de la secuencia que se lanzó. Este es un problema , porque el hilo que adquiere el bloqueo puede alterar el estado que depende del hilo pulsado. La solución es usar una condición while dentro de la instrucción de bloqueo

* Q = Queue.

Por eso, entiendo que cuando llamo al Pulse, hace 2 cosas antes de que termine. En primer lugar, elimina un hilo de la Q en espera para la Q lista. En segundo lugar, permite que un hilo (sin saber quién es ese hilo) en el Ready Q adquiera el bloqueo; no importa quién adquiere el candado (el hilo que vino de la Q en espera o un hilo que estaba en la Q lista por algún motivo).

Si tengo razón en ese entonces por qué está poniendo un while antes Monitor.Wait ayudar a solucionar el problema (problema - el pulso termina incluso si el hilo que venía de la Q de espera no adquirió la cerradura)?

A. Dime si estoy en lo cierto con el propósito de Monitor.Pulse.

B. ¿Por qué necesito para poner una while antes Monitor.Wait

El código completo de la respuesta a continuación:

class Program 
{ 
    static Queue<int> queue = new Queue<int>(); 
    static object someMonitor = new object(); 

    static void Main(string[] args) 
    { 
     Thread Thread1 = new Thread(WorkAlltheTime); 
     Thread1.Name = "Thread1"; 
     Thread Thread2 = new Thread(WorkAlltheTime); 
     Thread2.Name = "Thread2"; 
     Thread Thread3 = new Thread(WorkOnce); 
     Thread3.Name = "Thread3"; 
     Thread1.Start(); 
     Thread2.Start(); 
     Thread.Sleep(1000); 
     Thread3.Start(); 
     Console.ReadLine(); 
    } 

    static void WorkAlltheTime() 
    { 
     Console.WriteLine("Came in to Ready Q: " + Thread.CurrentThread.Name); 
     lock (someMonitor) 
     { 
      Console.WriteLine("Came out from Ready Q: " + Thread.CurrentThread.Name); 
      // Broken! 
      while (queue.Count == 0) 
      { 
       Console.WriteLine("Came in to Waiting Q: " + Thread.CurrentThread.Name); 
       Monitor.Wait(someMonitor); 
       Console.WriteLine("Came out from Waiting Q: " + Thread.CurrentThread.Name); 
      } 
      queue.Dequeue(); 
      Console.WriteLine("Thread: "+Thread.CurrentThread.Name+" Pulled Out"); 
     } 
    } 

    static void WorkOnce() 
    { 
     lock (someMonitor) 
     { 
      queue.Enqueue(1); 
      Monitor.Pulse(someMonitor); 
     } 
    } 
} 

Respuesta

9

Imagínese que usted está tratando de escribir una cola productor/consumidor - que Pulse cada vez que se produzca un artículo, y un consumidor necesita esperar hasta que haya un artículo para consumir. Usted escribiría código como este:

Foo item; 
lock(someMonitor) 
{ 
    while (queue.Count == 0) 
    { 
     Monitor.Wait(someMonitor); 
    } 
    item = queue.Dequeue(); 
} 
// Use the item 

Supongamos que no tiene el bucle while, y en su lugar escribió:

Foo item; 
lock(someMonitor) 
{ 
    // Broken! 
    if (queue.Count == 0) 
    { 
     Monitor.Wait(someMonitor); 
    } 
    item = queue.Dequeue(); 
} 
// Use the item 

Ahora supongamos que tiene un hilo ya la espera, y luego otra hilo justo antes de la instrucción de bloqueo ... entonces un productor pulsa el monitor (y agrega un elemento a la cola, por supuesto).

En ese punto, es completamente factible que el hilo que aún no ha llegado a la cerradura sea el primero en adquirir la cerradura ... momento en el cual el hilo "en espera" adquiere la cerradura, la cola estaría vacía de nuevo. Con solo una sola instrucción if, sin bucle, terminaría decalentándose cuando la cola esté vacía, lo que fallaría.

Con el ciclo while, esperará de nuevo hasta que se produzca el siguiente elemento, que es lo que realmente desea.

+0

Gracias, escribí el código completo de su ejemplo y con su explicación lo probé 90 veces y lo entiendo completamente ahora. Gracias Agian! –

Cuestiones relacionadas