12

两者的正确用法是什么,BlockingCollection或者ConcurrentQueue您可以自由地使项目出队而不会使用线程烧毁一半或更多的 CPU?

我正在使用 2 个线程运行一些测试,除非我的 Thread.Sleep 至少为 50~100 毫秒,否则它总是会占用至少 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 就不会爆炸。

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

4

3 回答 3

23

这不是因为BlockingCollectionor ConcurrentQueue,而是因为 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.
}
于 2011-07-01T09:28:06.957 回答
7

在这种情况下,您真的想使用BlockingCollection该类。它旨在阻止直到项目出现在队列中。这种性质的集合通常称为阻塞队列。这种特定的实现对于多个生产者多个消费者来说是安全的。如果您尝试自己实现它,那将是非常难以做到的事情。如果您使用BlockingCollection.

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

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

于 2011-07-01T12:59:21.953 回答
0

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

否则你应该考虑使用事件。

于 2011-07-01T09:30:37.000 回答