Collezioni contemporanee che mangiano troppa CPU senza Thread. Dormire

Quale sarebbe l’utilizzo corretto di entrambi, BlockingCollection o ConcurrentQueue modo da poter rimuovere in modo automatico gli elementi senza bruciare metà o più della CPU utilizzando un thread?

Stavo eseguendo alcuni test usando 2 thread e, a meno che non avessi un Thread.Sleep di almeno 50 ~ 100ms, avrebbe sempre colpito almeno il 50% della mia CPU.

Ecco un esempio immaginario:

 private void _DequeueItem() { object o = null; while(socket.Connected) { while (!listOfQueueItems.IsEmpty) { if (listOfQueueItems.TryDequeue(out o)) { // use the data } } } } 

Con l’esempio precedente dovrei impostare un thread.sleep in modo che la CPU non esploda.

Nota: ho anche provato senza il tempo per il controllo IsEmpty, il risultato è stato lo stesso.

    Non è a causa di BlockingCollection o ConcurrentQueue , ma il ciclo while:

     while(socket.Connected) { while (!listOfQueueItems.IsEmpty) { /*code*/ } } 

    Ovviamente ci vorrà la cpu; a causa di se la coda è vuota, allora il ciclo while è come:

     while (true) ; 

    che a sua volta mangerà le risorse della cpu.

    Questo non è un buon modo per usare ConcurrentQueue , dovresti usare AutoResetEvent con esso in modo che ogni volta che viene aggiunto un articolo ti venga notificato. Esempio:

     private ConcurrentQueue _queue = new ConcurrentQueue(); private AutoResetEvent _queueNotifier = new AutoResetEvent(false); //at the producer: _queue.Enqueue(new Data()); _queueNotifier.Set(); //at the consumer: while (true)//or some condition { _queueNotifier.WaitOne();//here we will block until receive signal notification. Data data; if (_queue.TryDequeue(out data)) { //handle the data } } 

    Per un buon utilizzo di BlockingCollection è necessario utilizzare GetConsumingEnumerable() per attendere che gli elementi vengano aggiunti, ad esempio:

     //declare the buffer private BlockingCollection _buffer = new BlockingCollection(new ConcurrentQueue()); //at the producer method: _messageBuffer.Add(new Data()); //at the consumer foreach (Data data in _buffer.GetConsumingEnumerable())//it will block here automatically waiting from new items to be added and it will not take cpu down { //handle the data here. } 

    Vuoi davvero usare la class BlockingCollection in questo caso. È progettato per bloccare fino a quando un elemento viene visualizzato nella coda. Una raccolta di questa natura viene spesso definita una coda di blocco. Questa particolare implementazione è sicura per più produttori e più consumatori. È qualcosa di sorprendentemente difficile da ottenere se si è tentato di implementarlo da soli. Ecco come sarà il tuo codice se hai usato BlockingCollection .

     private void _DequeueItem() { while(socket.Connected) { object o = listOfQueueItems.Take(); // use the data } } 

    Il metodo Take blocca automaticamente se la coda è vuota. Blocca in un modo che mette il thread nello stato SleepWaitJoin modo che non consuma risorse della CPU. La cosa bella di BlockingCollection è che utilizza anche strategie a basso lock per aumentare le prestazioni. Ciò significa che Take controllerà se è presente un elemento nella coda e in caso contrario eseguirà una breve attesa per impedire un interruttore di contesto del thread. Se la coda è ancora vuota, metterà in attesa il thread. Ciò significa che BlockingCollection avrà alcuni dei vantaggi in termini di prestazioni ConcurrentQueue in termini di esecuzione simultanea.

    È ansible chiamare Thread.Sleep() solo quando la coda è vuota:

     private void DequeueItem() { object o = null; while(socket.Connected) { if (listOfQueueItems.IsEmpty) { Thread.Sleep(50); } else if (listOfQueueItems.TryDequeue(out o)) { // use the data } } } 

    Altrimenti dovresti considerare di usare gli eventi.