2012-01-16 18 views
7

Estoy implementando una plataforma de chat basada en la web en la aplicación web ASP.NET, y uso una técnica similar a la del sondeo largo. Quiero decir, mantengo cada solicitud web del cliente durante un período de tiempo específico (tiempo de espera) o hasta que llega un mensaje nuevo, y luego se envía una respuesta al cliente.uso de la CPU aumentando hasta 100% en bucle infinito en el hilo

Mantengo clientes conectados en la memoria (objeto de diccionario) y cada vez que se envía un mensaje nuevo a un cliente, escribo este mensaje en la matriz de mensajes del cliente receptor. El cliente necesita enviar una solicitud para obtener sus propios mensajes, y guardo esta solicitud en una matriz en la memoria.

Estoy usando el manejador http asincrónico para la solicitud del cliente de escuchas, estoy manteniendo las solicitudes web en una matriz en la memoria. Utilizo hilos para verificar continuamente nuevos mensajes de la memoria (en el diccionario que se crea para cada cliente).

No consumo .net hilos grupo de subprocesos para comprobar si hay nuevos mensajes o con tiempo excedido requests.I web crear hilos de la siguiente manera:

System.Threading.Thread t = new Thread(new ThreadStart(QueueCometWaitRequest_WaitCallback)); 
t.IsBackground = false; 
t.Start(); 

En el método QueueCometWaitRequest_WaitCallback de cada hilo estoy en un bucle while:

while (true) 
{ 
... 
Thread.Sleep(100); 
} 

en este método, yo soy el registro de tiempo de solicitud web a cabo o un nuevo mensaje para cada solicitud web que también se mantiene en una matriz en la memoria.

Todo estaba funcionando bien hasta que noté que el uso de la CPU llegaba al 100% en el tiempo. (en minutos después del primer cliente conectado) Al comienzo de la primera solicitud, todo parece ser normal, es decir, el uso de la CPU no es superior al 10% al devolver una respuesta al cliente. Pero a tiempo, incluso con 2 clientes, el uso de la CPU aumenta hasta el 100%. Parece que el uso de la CPU es del 100% solo cuando se escribe en una respuesta para una solicitud del cliente. Si no queda ningún cliente, todo vuelve a ser normal (el uso de la CPU es de aproximadamente 0%) hasta que un cliente realice una nueva solicitud web.

No conozco los hilos en detalle, pero sospecho de los nuevos hilos que he creado y que funcionan infinitamente. Es como que el sistema operativo les da más uso de CPU y recursos a tiempo ya que están trabajando todo el tiempo, y este Thread.Sleep (100) no funciona.

Aquí es el método QueueCometWaitRequest_WaitCallback():

void QueueCometWaitRequest_WaitCallback() 
{ 
    while (true) 
    { 
     if (processRequest.Length == 0) 
     { 
      Thread.Sleep(100); 
     } 
     else 
     { 
      for (int i = 0; i < processRequest.Length; i++) 
      { 
       Thread.Sleep(100); 

       // below I am checking for new message or request time out 
       ................. 
       ................. 

       // If new message or time out I write to response 
      } 
     }  
    } 
} 

Espero que pueda explicar la situación, y estoy abierto a cualquier sugerencia, así (como la implementación de una manera diferente)

Si me puede ayudar con este problema lo agradeceré con gratitud, Gracias

+1

¿Entonces, básicamente, debe verificar los nuevos mensajes cada N-milisegundos? ¿Es todo o algo más debe hacerse asincrónico? Si es así solo use async 'System.Threading.Timer' /' System.Timers.Timer' que se activará cada N-milisegundos. – sll

+0

¿Es esto aproximadamente lo que estás tratando de lograr? while (true) {Thread.Sleep (100); foreach (var req en processRequest) {performProcessRequest (req);} processRequest.Remove (r => r.RequestCompletedOrTimedOut);} –

+0

@sll: Necesito devolver la respuesta a un cliente (mensaje nuevo) ASAP
Dado que este es un chat aplicación, el período de verificación no debe ser demasiado largo, creo que debe ser menor de 1 segundo si utilizo el temporizador? – Mehmet

Respuesta

9

Al igual que un comentario general de las mejores prácticas en lugar de una respuesta directa, no es aconsejable escribir un Thread.Sleep (100) dentro de su mensaje receptor de hilo Un mejor método sería utilizar Thread.Join como se menciona anteriormente o los manejadores de espera ManualResetEvent. Por ejemplo, se puede codificar así:

private ManualResetEvent waitHandle; 
private object syncRoot = new object(); 
private bool isRunning = false; 

void CreateThread() 
{ 
    this.waitHandle = new ManualResetEvent(false); 

    isRunning = true; // Set to false to kill the thread 
    System.Threading.Thread t = new Thread(new ThreadStart(QueueCometWaitRequest_WaitCallback));   
    t.IsBackground = false; 
    t.Start(); 
} 

void PushData() 
{ 
    // On incoming data, push data into the processRequest queue and set the waithandle 
    lock(syncRoot) 
    { 
     processRequest.Add(/* ... your data object to process. Assumes this is a queue */); 
     waitHandle.Set(); // Signal to the thread there is data to process 
    } 
} 

void QueueCometWaitRequest_WaitCallback() 
{  
    while (isRunning)  
    {  
     // Waits here using 0% CPU until the waitHandle.Set is called above 
     this.waitHandle.WaitOne(); 

     // Ensures no-one sets waithandle while data is being processed and 
     // subsequently reset 
     lock(syncRoot) 
     { 
      for (int i = 0; i < processRequest.Length; i++)   
      {       
       // Process the message. 
       // What's the type of processRequest? Im assuming a queue or something  
      }  

      // Reset the Waithandle for the next requestto process 
      this.waitHandle.Reset(); 
     } 
    }   
} 

Esto garantizaría que el hilo 0% utiliza la CPU mientras espera y sólo consume la CPU cuando hay trabajo que hacer.

¿Ha fallado haber pensado en una solución de terceros para la mensajería bidireccional asincrónica? He usado RabbitMQ (AMQP) con gran éxito en.Aplicaciones NET para manejar mensajes de alto rendimiento. La API para RabbitMQ significa que recuperas un evento cuando se recibe un mensaje que luego se puede procesar en una cadena de fondo.

Saludos,

+1

Gracias por su respuesta. Pero luego llega otra pregunta: necesito devolver una respuesta al cliente, incluso si no hay un nuevo mensaje para un período de tiempo de espera determinado. En este caso, ¿cómo puedo waitHandle.Set() ;? Quiero decir, ¿cómo puedo saber para iniciar los hilos? – Mehmet

+0

Sin problemas. Solo estaba pensando: no debería obtener 100% de CPU de su ejemplo de código. ¿Cuántos hilos estás creando? (Sólo una corazonada). debería ser uno por supuesto! No es uno por solicitud ¿verdad? –

+0

No uno por solicitud, Total de 5 hilos. Las solicitudes se encuentran en una matriz – Mehmet

0

que mantienen los clientes conectados en la memoria (objeto de diccionario)

objetos diccionario no es seguro para subprocesos si se utiliza de forma estática. Si lo usa como un miembro estático, entonces necesita crear una declaración de bloqueo.

A continuación, se muestra un ejemplo extraído de una clase Log4Net LoggerFactory ... Tenga en cuenta que TypeToLoggerMap es un objeto de diccionario y cuando se hace referencia al método GetLogger, se utiliza una instrucción Lock.

public static class LoggerFactory 
{ 
    public static ILogger GetLogger(Ninject.Activation.IContext context) 
    { 
     return GetLogger(context.Request.Target == null ? typeof(ILogger) : context.Request.Target.Member.DeclaringType); 
    } 

    private static readonly Dictionary<Type, ILogger> TypeToLoggerMap = new Dictionary<Type, ILogger>(); 

    private static ILogger GetLogger(Type type) 
    { 
     lock (TypeToLoggerMap) 
     { 
      if (TypeToLoggerMap.ContainsKey(type)) 
       return TypeToLoggerMap[type]; 

      ILogger logger = new Logger(type); 
      TypeToLoggerMap.Add(type, logger); 

      return logger; 
     } 
    } 
} 

Revise este artículo - aquí descubrí la información anterior sobre los objetos del Diccionario.

https://www.toptal.com/dot-net/hunting-high-cpu-usage-in-dot-net

Como nota al margen, ¿ha considerado el uso de SignalR para su proyecto?