并发集合在没有Thread.Sleep的情况下占用了太多的cpu



BlockingCollectionConcurrentQueue的正确用法是什么,这样您就可以自由地将项目出列,而不会使用线程消耗掉一半或更多的CPU

我用两个线程运行一些测试,除非我有一个线程。睡眠至少50~100ms,它总是会占用我50%的CPU。

这里有一个虚构的例子:

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

对于上面的例子,我必须设置一个thread.sleep,这样cpu就不会爆炸。

注意:我也尝试过,但没有检查IsEmpty,结果是一样的。

这不是因为BlockingCollectionConcurrentQueue,而是while循环:

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

当然,它会降低cpu;因为如果队列是空的,那么while循环就像:

while (true) ;

这反过来又会消耗cpu资源。

这不是使用ConcurrentQueue的好方法,您应该将AutoResetEvent与之一起使用,这样无论何时添加项目,都会收到通知。示例:

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

为了更好地使用BlockingCollection,您应该使用GetConsumingEnumerable()来等待添加项目,例如:

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

在这种情况下,您确实希望使用BlockingCollection类。它被设计为阻止,直到某个项目出现在队列中。这种性质的集合通常被称为阻塞队列。这种特定的实现对于多个生产者多个消费者来说是安全的。如果你自己尝试实现,这是一件很难做到的事情。以下是如果您使用BlockingCollection,您的代码会是什么样子。

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

如果队列为空,Take方法会自动阻止。它以将线程置于SleepWaitJoin状态的方式进行阻塞,这样就不会消耗CPU资源。BlockingCollection的巧妙之处在于它还使用了低锁定策略来提高性能。这意味着Take将检查队列中是否有项目,如果没有,它将短暂地执行旋转等待以防止线程的上下文切换。如果队列仍然是空的,那么它将使线程进入睡眠状态。这意味着BlockingCollection将具有ConcurrentQueue在并发执行方面提供的一些性能优势。

只有当队列为空时才能调用Thread.Sleep()

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

否则,您应该考虑使用事件。

相关内容

  • 没有找到相关文章

最新更新