2012-06-11 23 views
6

Estoy ejecutando este código y está usando una buena cantidad de CPU a pesar de que no hace absolutamente nada la mayor parte del tiempo.¿Cómo bloquear una operación hasta que se cumpla una condición?

while (this.IsListening) 
{ 
    while (this.RecievedMessageBuffer.Count > 0) 
    { 
     lock (this.RecievedMessageBuffer) 
     { 
      this.RecievedMessageBuffer[0].Reconstruct(); 
      this.RecievedMessageBuffer[0].HandleMessage(messageHandler); 
      this.RecievedMessageBuffer.RemoveAt(0); 
     } 
    } 
} 

¿Cuál es la mejor manera de bloquear hasta que se cumpla una condición?

+4

'Thread.Sleep()'? –

+0

Thread.Sleep() también bloquea la IU. –

+4

@RanhiruCooray no, esa es casi siempre la respuesta * incorrecta *. Hay estructuras dedicadas a este trabajo, como el WaitHandle sugerido por Mark. Ver http://stackoverflow.com/questions/9417260/when-is-it-sensible-to-use-thread-sleep –

Respuesta

7

Suponiendo que está utilizando .NET 4, sugiero cambiar RecievedMessageBuffer para que sea BlockingCollection. Cuando inserte mensajes, llame al método Add. Cuando desee recuperar un mensaje, llame a los métodos Take o TryTake. Take bloqueará el hilo de lectura hasta que haya un mensaje disponible, sin grabar la CPU como en el ejemplo original.

// Somewhere else 
BlockingCollection<SomethingLikeAMessage> RecievedMessageBuffer = new BlockingCollection<SomethingLikeAMessage>(); 


// Something like this where your example was 
while (this.IsListening) 
{ 
    SomethingLikeAMessage message; 
    if (RecievedMessageBuffer.TryTake(out message, 5000); 
    { 
     message.Reconstruct(); 
     message.HandleMessage(messageHandler); 
    } 
} 
+1

¡Muchas gracias! eso es perfecto para lo que necesitaba! Estaba usando una colección Syncronized <> antes pero como BlockingCollection <> también es seguro para subprocesos, ¡esto es perfecto para lo que estoy haciendo! ¡Todo está funcionando fantásticamente bien ahora! – MJLaukala

9

Utilice un WaitHandle.

WaitHandle waitHandle = new AutoResetEvent(); 

// In your thread. 
waitHandle.WaitOne(); 

// In another thread signal that the condition is met. 
waitHandle.Set(); 

También podría considerar cambiar la interfaz de su clase para plantear un evento cuando hay nuevos datos para leer. Luego puede poner su código dentro del controlador de eventos.

+0

Tendré que hacer algunas pruebas con WaitHandles. Nunca los conocí antes de hoy. Nunca hice algo demasiado serio con multihilo antes de hace unas semanas. – MJLaukala

+0

No pude usar 'WaitHandle' con el método' Set() '. Creo que debería haber al menos 'EventWaitHandle' en su lugar. – Gucu112

2

Las líneas de código anteriores y específicamente AutoResetEvent están disponibles en la versión 3.5. Así que el código simple como el anterior con algunas correcciones menores es muy efectivo porque funciona y está cerca de la API básica. La corrección debe ser

AutoResetEvent waitHandle = new AutoResetEvent (false); El constructor con argumento falso hace que WaitOne() espere porque AutoResetEven no se restablece (falso). No hay mucha ventaja de usar la interfaz WaitHandle, así que simplemente usaría AutoResetEvent en su lugar ya que expone el método Set y WaitOne es bastante detallado en este caso. Lo más importante, el argumento constructor y debe ser falso.

Cuestiones relacionadas