Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concurrent collections eating too much cpu without Thread.Sleep

What would be the correct usage of either, BlockingCollection or ConcurrentQueue so you can freely dequeue items without burning out half or more of your CPU using a thread ?

I was running some tests using 2 threads and unless I had a Thread.Sleep of at least 50~100ms it would always hit at least 50% of my CPU.

Here is a fictional example:

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

With the above example I would have to set a thread.sleep so the cpu doesnt blow up.

Note: I have also tried it without the while for IsEmpty check, result was the same.

like image 692
Prix Avatar asked Jul 01 '11 09:07

Prix


People also ask

Does thread sleep consume CPU?

Then you can be sure if the thread was actually sleeping or was doing something else. To be more clear: Threads consume no CPU at all while in not runnable state. Not even a tiny bit. This does not depend on implementation.

What is the correlation between threads and CPU utilization?

It depends on the jobs the threads are doing. A program with one thread can consume 100% of CPU and a program with lots of threads can consume less. If you are looking for an optimized relation between threads and job done, you must study your case, and possibly found an empiric solution.

Does thread sleep block other threads?

Sleep method causes the current thread to immediately block for the number of milliseconds or the time interval you pass to the method, and yields the remainder of its time slice to another thread. Once that interval elapses, the sleeping thread resumes execution. One thread cannot call Thread. Sleep on another thread.


1 Answers

It is not because of the BlockingCollection or ConcurrentQueue, but the while loop:

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

Of course it will take the cpu down; because of if the queue is empty, then the while loop is just like:

while (true) ;

which in turn will eat the cpu resources.

This is not a good way of using ConcurrentQueue you should use AutoResetEvent with it so whenever item is added you will be notified. Example:

private ConcurrentQueue<Data> _queue = new ConcurrentQueue<Data>();
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
    }
}

For a good usage of the BlockingCollection you should use the GetConsumingEnumerable() to wait for the items to be added, Like:

//declare the buffer
private BlockingCollection<Data> _buffer = new BlockingCollection<Data>(new ConcurrentQueue<Data>());

//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.
}
like image 188
Jalal Said Avatar answered Sep 22 '22 00:09

Jalal Said