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

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

我使用2个线程运行一些测试,除非我有一个至少50~100ms的Thread.Sleep,它总是至少达到我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检查的时间,结果是一样的。

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

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

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

 while (true) ; 

反过来会占用cpu资源。

这不是使用ConcurrentQueue的好方法,您应该使用AutoResetEvent ,因此无论何时添加项目,您都会收到通知。 例:

 private ConcurrentQueue _queue = new ConcurrentQueue(); 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 _buffer = new BlockingCollection(new ConcurrentQueue()); //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将检查队列中是否有项目,如果没有,那么它将暂时执行旋转等待以防止线程的上下文切换。 如果队列仍为空,那么它将使线程进入hibernate状态。 这意味着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 } } } 

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