对BlockingCollection
或ConcurrentQueue
的正确用法是什么,这样您就可以自由地将项目出列,而不会使用线程烧掉一半或更多的CPU? < / p>
我正在使用2个线程运行一些测试,除非我的Thread.Sleep至少为50~100ms,否则它至少会占到我CPU的50%。
这是一个虚构的例子:
private void _DequeueItem()
{
object o = null;
while(socket.Connected)
{
while (!listOfQueueItems.IsEmpty)
{
if (listOfQueueItems.TryDequeue(out o))
{
// use the data
}
}
}
}
通过上面的例子,我必须设置一个thread.sleep,这样cpu就不会爆炸了。
注意:我也尝试了没有用于IsEmpty检查的时间,结果是一样的。
答案 0 :(得分:23)
这不是因为BlockingCollection
或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.
}
答案 1 :(得分:7)
在这种情况下,你真的想要使用BlockingCollection
类。它被设计为阻塞,直到项目出现在队列中。这种性质的集合通常被称为阻塞队列。对于多个生产者和多个消费者而言,此特定实施是安全的。如果你自己尝试实现它,那就很难做到。如果您使用BlockingCollection
,则以下是您的代码。
private void _DequeueItem()
{
while(socket.Connected)
{
object o = listOfQueueItems.Take();
// use the data
}
}
如果队列为空,Take
方法会自动阻止。它以一种将线程置于SleepWaitJoin
状态的方式阻塞,这样它就不会消耗CPU资源。关于BlockingCollection
的巧妙之处在于它还使用低锁策略来提高性能。这意味着Take
将检查队列中是否有项目,如果没有,则简要执行旋转等待以防止线程的上下文切换。如果队列仍为空,那么它将使线程进入休眠状态。这意味着BlockingCollection
将具有ConcurrentQueue
在并发执行方面提供的一些性能优势。
答案 2 :(得分: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
}
}
}
否则你应该考虑使用事件。