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

发布于 2024-11-18 04:19:33 字数 622 浏览 4 评论 0原文

BlockingCollectionConcurrentQueue 的正确用法是什么,这样您就可以自由地将项目出队,而不会使用线程消耗一半或更多的 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 就不会崩溃。

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

What would be the correct usage of either, BlockingCollection or ConcurrentQueue so you can freely dequeue items without burning out half or more of your CPU using a thread ?

I was running some tests using 2 threads and unless I had a Thread.Sleep of at least 50~100ms it would always hit at least 50% of my CPU.

Here is a fictional example:

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

With the above example I would have to set a thread.sleep so the cpu doesnt blow up.

Note: I have also tried it without the while for IsEmpty check, result was the same.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蓝色星空 2024-11-25 04:19:33

不是因为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.
}

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.
}
卖梦商人 2024-11-25 04:19:33

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

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

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

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.

酒解孤独 2024-11-25 04:19:33

仅当队列为空时才可以调用 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
        }
    }
}

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文