2012-01-16 9 views
7

Sto implementando una piattaforma di chat basata sul Web nell'applicazione Web ASP.NET e utilizzo una tecnica simile al polling lungo. Voglio dire, tengo ogni richiesta web dal client per un periodo di tempo specifico (timeout) o fino a quando arriva un nuovo messaggio, e quindi la risposta viene inviata al client.utilizzo della cpu che aumenta fino al 100% nel ciclo infinito nella filettatura

Conservo i client connessi in memoria (oggetto dizionario) e quando un nuovo messaggio viene inviato a un client, scrivo questo messaggio nell'array dei messaggi del client ricevente. Il client deve inviare una richiesta per ottenere i propri messaggi e mantengo questa richiesta in una matrice in memoria.

Sto utilizzando il gestore HTTP asincrono per la richiesta del client di ascolto, sto mantenendo le richieste Web in una matrice in memoria. Uso i thread per verificare continuamente i nuovi messaggi dalla memoria (nel dizionario creato per ciascun client).

Non faccio uso di .NET discussioni pool di thread per verificare la presenza di nuovi messaggi o cronometrato fuori requests.I web di creare discussioni in questo modo:

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

Nel metodo QueueCometWaitRequest_WaitCallback di ogni thread mi trovo in un infinito ciclo while:

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

in questo metodo, sto controllando per la richiesta web time out o un nuovo messaggio per ogni richiesta Web, che è anche tenuto in una matrice in memoria.

Tutto funzionava bene fino a quando ho notato che l'utilizzo della CPU raggiunge il 100% nel tempo. (in minuti dopo il primo client connesso) All'inizio della prima richiesta tutto sembra essere normale, voglio dire che l'utilizzo della CPU non è superiore al 10% mentre si restituisce una risposta al client. Ma con il tempo anche con 2 client l'utilizzo della CPU aumenta fino al 100%. Sembra che l'utilizzo della CPU sia al 100% solo quando si scrive una risposta per una richiesta del client. Se non viene lasciato alcun client, tutto torna a essere normale (l'utilizzo della CPU è pari a circa lo 0%) fino a quando una nuova richiesta Web non viene eseguita da un client.

Non conosco i thread in dettaglio, ma sono sospettoso riguardo ai nuovi thread che ho creato e che funziona all'infinito. È come se il sistema operativo fornisse loro più risorse e risorse in termini di tempo poiché funzionavano sempre e questo Thread.Sleep (100) non funziona.

Ecco il metodo 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 
      } 
     }  
    } 
} 

Spero di poter spiegare la situazione, e sono aperto a qualsiasi suggerimento così (come implementare in modo diverso)

Se mi può aiutare con questo problema apprezzerò riconoscente, Grazie

+1

Quindi, in pratica, è necessario verificare la presenza di nuovi messaggi ogni millisecondo N? È tutto o qualcos'altro dovrebbe essere fatto asincrono? Se è così basta usare async 'System.Threading.Timer' /' System.Timers.Timer' che dovrebbe attivare ogni N millisecondi. – sll

+0

Questo è approssimativamente quello che stai cercando di realizzare? while (true) {Thread.Sleep (100); foreach (var req in processRequest) {performProcessRequest (req);} processRequest.Remove (r => r.RequestCompletedOrTimedOut);} –

+0

@sll: Devo restituire la risposta a un client (nuovo messaggio) ASAP
Poiché si tratta di una chat applicazione, il periodo di controllo non dovrebbe essere troppo lungo, penso che debba essere inferiore a 1 secondo se utilizzo il timer? – Mehmet

risposta

9

Proprio come un commento di buone pratiche generali in contrapposizione alla risposta diretta - non è consigliabile scrivere un Thread.Sleep (100) all'interno del tuo thread del destinatario del messaggio. Un metodo migliore sarebbe utilizzare Thread.Join come maniglie di attesa ManualResetEvent menzionate in precedenza. Per esempio, si potrebbe codice come questo:

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(); 
     } 
    }   
} 

Questo dovrebbe garantire che il vostro thread utilizza 0% della CPU durante l'attesa e consuma solo CPU quando c'è del lavoro da fare.

In caso contrario, avete mai pensato a una soluzione di terze parti per la messaggistica bidirezionale asincrona? Ho usato RabbitMQ (AMQP) con grande successo in.Applicazioni NET per gestire la messaggistica ad alto throughput. L'API per RabbitMQ ti consente di recuperare un evento quando un messaggio è stato ricevuto e può quindi essere elaborato su un thread in background.

Con i migliori saluti,

+1

Grazie per la risposta. Ma poi arriva un'altra domanda: devo restituire una risposta al cliente anche se non c'è un nuovo messaggio per un determinato periodo di timeout. In questo caso, come posso aspettareHandle.Set() ;? Voglio dire, come posso sapere iniziare i thread? – Mehmet

+0

Nessun problema. Stavo solo pensando: non dovresti ottenere una CPU al 100% dal tuo esempio di codice. Quanti thread stai creando? (Solo un sospetto). dovrebbe essere uno naturalmente! Non è uno per richiesta è? –

+0

Non uno per richiesta, totale di 5 thread. Le richieste sono in un array – Mehmet

0

ho mantenere i clienti collegati in memoria (oggetto dizionario)

oggetti Dictionary non sono thread sicuro se usato in modo statico. Se lo si utilizza come membro statico, è necessario creare un'istruzione Lock.

Ecco un esempio sollevato da una classe LogNetNet LoggerFactory ... Si noti che TypeToLoggerMap è un oggetto dizionario e quando viene fatto riferimento vai al metodo GetLogger, viene utilizzata un'istruzione 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; 
     } 
    } 
} 

Controllare questo articolo - questo è dove ho scoperto le informazioni di cui sopra sugli oggetti del dizionario.

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

Come nota a margine, avete considerato utilizzando SignalR per il vostro progetto?

Problemi correlati