0

对队列使用无限循环时tryDequeue,会占用过多的 CPU。

当队列不为空时,是否可以将此操作作为用户界面通知执行?然后运行它一个事件或函数?

我得出的结论是,在我使用 ZeroMQ 库的实现中,在主要负责发布的线程中,无法避免无限循环。

using (var context = new Context(1)){
                    using (Socket client = context.Socket(SocketType.PUB)){
while (!StopAllThread){
   SQLQueue.TryDequeue(out message);
   if (message != null){
   ...
   }
}
}
4

3 回答 3

2

目前尚不清楚您使用什么 API 或技术,但我想应该有一个阻塞替代方案,例如

var message = SQLQueue.Dequeue();

在一般情况下,这将阻塞线程,直到从队列中读取消息。当没有可读取的内容时,这不会消耗任何 CPU。

如果您使用 Zeromq(来自评论),服务器上有一个阻塞调用(取自此处

// ZMQ Context, server socket
using (ZmqContext context = ZmqContext.Create())
using (ZmqSocket server = context.CreateSocket(SocketType.REP))
{
    server.Bind("tcp://*:5555");

    while (true)
    {
        // Wait for next request from client
        string message = server.Receive(Encoding.Unicode);
        ...

评论

如果您将架构从主动轮询新消息更改为基于推送的模型,您将摆脱高 CPU 消耗的问题。在推送模型中,您有一个线程从队列中读取。如果那里没有消息,它会阻塞。读取消息后,您的代码将向消费者发送一个事件,表示已读取一条新消息,这是消费者要处理的消息。

看看Blocking Collection Take方法。

于 2013-08-26T08:27:26.740 回答
1

对这种BlockingCollection事情很有用。

例如:

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    public class Program
    {
        private readonly BlockingCollection<string> _queue = new BlockingCollection<string>();

        private void run()
        {
            Task.Run(() => producer());
            consumer();

            Console.WriteLine("Press <return> to exit.");
        }

        private void consumer()
        {
            Console.WriteLine("consumer() starting.");

            foreach (var item in _queue.GetConsumingEnumerable())
            {
                Console.WriteLine("consumer() processing item " + item);
            }

            Console.WriteLine("consumer() stopping.");
        }

        private void producer()
        {
            Console.WriteLine("producer() starting.");

            for (int i = 0; i < 20; ++i)
            {
                _queue.Add(i.ToString());
                Thread.Sleep(200);
            }

            Console.WriteLine("producer() finishing.");

            _queue.CompleteAdding(); // Calling this makes the consumer exit its foreach loop.
        }

        private static void Main(string[] args)
        {
            new Program().run();
        }

    }
}

请注意如何调用GetConsumingEnumerable()以获取可枚举,该可枚举在等待新项目出现在队列中时自动阻塞,但当队列通过调用CompleteAdding()生产者线程标记为已完成时立即结束。

于 2013-08-26T08:49:57.330 回答
0

您可以自定义Queue添加一些事件,然后您可以处理事件以通知有关入队和出队项目:

public class CustomQueue<T> : Queue<T>
{
    public event EventHandler ItemDequeued;
    public event EventHandler ItemEnqueued;
    public new void Enqueue(T item)
    {
        base.Enqueue(item);
        if (ItemEnqueued != null) ItemEnqueued(this, EventArgs.Empty);
    }
    public new T Dequeue()
    {
        T a = base.Dequeue();
        if (ItemDequeued != null) ItemDequeued(this, EventArgs.Empty);
        return a;
    }
}
//Then you can handle the event ItemDequeued to know if its Count = 0
yourQueue.ItemDequeued += (s, e) =>{
   if (yourQueue.Count == 0) MessageBox.Show("EMPTY queue!");
};
yourQueue.ItemEnqueued += (s,e) => {
   //you can try running code asynchronously here;
};
于 2013-08-26T08:32:51.987 回答