2010-08-17 11 views
9

Ehi, sto cercando di implementare ConcurrentQueue in C# per un server asincrono. Gli articoli vengono messi in coda non appena viene ricevuto un messaggio completo. Per deselezionare i messaggi, sto facendo un piccolo numero di thread per fare il lavoro di dequeueing e manutenzione delle richieste. Questo è inadeguato poiché ogni thread utilizza un ciclo while, che consuma piuttosto una grande quantità di tempo del processore, per ovvi motivi.Eliminazione di oggetti da una ConcurrentQueue in C#

Qualcuno dovrebbe conoscere un metodo per eliminare i messaggi quando necessario, ma non consumare così tanto tempo di elaborazione.

{ 
    ... 

    for (int i = 0; i < 3; i++) 
    { 
     Thread t = new Thread(new ThreadStart(startParsingMessages)); 
     t.Start(); 
    } 

    ... 
} 

private void startParsingMessages() 
{ 
    QueueContainer dequeued = null; 
    Console.WriteLine("Trying"); 
    while (true) 
    { 
     if (queue.TryDequeue(out dequeued)) 
     { 
      Console.WriteLine("processing queue"); 
      ProcessMessage(dequeued.socket, dequeued.message); 
     } 
    } 
} 

risposta

18

Invece di usare direttamente ConcurrentQueue<T>, hai provato avvolgendolo in un BlockingCollection<T>? Quindi è possibile utilizzare TryTake(out T, TimeSpan) ecc. Credo che sia l'uso previsto: che le raccolte simultanee stesse sarebbero di solito essere lì solo così è possibile selezionare come funziona la raccolta di blocchi.

Questo non deve essere l'unica impiego per queste raccolte, naturalmente, ma in particolare per ConcurrentQueue<T>, lo scenario di produttore/consumatore di coda è la più comune - a questo punto BlockingCollection<T> è il modo per rendere più facile fare la cosa giusta

+1

+1 Quasi implichi che le raccolte simultanee siano solo lì per tornare a 'BlockingCollection' e non sono sicuro che sia vero. Ma tu sei morto con l'uso di 'BlockingCollection' qui per ridurre al minimo tutta la cerimonia per andare avanti con i tradizionali modelli produttore/consumatore. – Marc

+0

@Marc: Non direi che è abbastanza vero fino a quel punto, ma penso che sarà l'uso * predominante *. Aggiornerò la risposta –

+0

Ciao, grazie per i suggerimenti! Ho avvolto il mio ConcurrentQueue in BlockingCollection ei risultati sono fantastici. Sto eseguendo 20 thread contemporaneamente per mangiare attraverso la coda, con un tempo di blocco di 0,1 secondi per thread tramite il metodo TryTake (out T, TimeSpan s), e sta funzionando con un certo fascino. La richiesta viene sottoposta a manutenzione entro un lasso di tempo appropriato (più veloce che senza il concurrentqueue) e il carico della CPU non è mai stato inferiore. Grazie ancora! – user352891

4

È possibile utilizzare un oggetto di blocco statico per i thread in attesa e quindi emetterlo a impulsi quando qualcosa è pronto per essere elaborato.

static readonly object queueLock = new object(); 

// Threads that enqueue an object 
void QueueMessage() { 
    lock (queueLock) { 
    queue.Enqueue(obj); 
    Monitor.Pulse(queueLock); 
    } 
} 
// Thread dequeuer 
private void startParsingMessages() { 
    QueueContainer dequeued = null; 
    Console.WriteLine("Trying"); 
    while (true) { 
    lock(queueLock) { 
     if (!queue.TryDequeue(out dequeued)) { 
     Console.WriteLine("No object to dequeue, waiting..."); 
     // Threads will wait here and only one will be released when .Pulse()d 
     Monitor.Wait(queueLock); 
     dequeued = queue.Dequeue(); 
     } 
     if (dequeued != null) { 
     Console.WriteLine("processing queue"); 
     ProcessMessage(dequeued.socket, dequeued.message); 
     } 
    } 
    } 
} 

Tenete a mente che questo può diventare piuttosto complicato con i più rami che si possiede, ma l'essenza di esso è, si blocca su un oggetto comune e chiamare Monitor.Wait aspettare su un oggetto per essere Pulse d. Quando questo accade, verrà rilasciato solo un thread che lo stava aspettando. Se accodate molti oggetti e volete che tutti vadano, potete chiamare Monitor.PulseAll che rilascerà tutti i thread che erano in attesa sull'oggetto.

+1

Ciao, grazie per il suggerimento!Ho implementato il tuo suggerimento ed i risultati sono stati molto promettenti con un piccolo numero di richieste. Tuttavia, non appena ci fu un gran numero di richieste di assistenza, il tempo necessario per rispondere aumentò notevolmente. Ancora una volta, grazie per il suggerimento, è sicuramente una tecnica che terrò a mente per i progetti futuri. – user352891