Concurrent collections eating too much cpu without Thread.Sleep

送分小仙女□ 提交于 2019-12-01 15:17:10

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.
}

You really want to be using the BlockingCollection class in this case. It is designed to block until an item appears in the queue. A collection of this nature is often referred to as a blocking queue. This particular implementation is safe for multiple producers and multiple consumers. That is something that is surprisingly difficult to get right if you tried implementing it yourself. Here is what your code would look like if you used BlockingCollection.

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

The Take method blocks automatically if the queue is empty. It blocks in a manner that puts the thread in the SleepWaitJoin state so that it will not consume CPU resources. The neat thing about BlockingCollection is that it also uses low-lock strategies to increase performance. What this means is that Take will check to see if there is an item in the queue and if not then it will briefly perform a spin wait to prevent a context switch of the thread. If the queue is still empty then it will put the thread to sleep. This means that BlockingCollection will have some of the performance benefits that ConcurrentQueue provides in regards to concurrent execution.

You can call Thread.Sleep() only when queue is empty:

private void DequeueItem()
{
    object o = null;

    while(socket.Connected)
    {
        if (listOfQueueItems.IsEmpty)
        {
            Thread.Sleep(50);
        }
        else if (listOfQueueItems.TryDequeue(out o))
        {
            // use the data
        }
    }
}

Otherwise you should consider to use events.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!