如何在C#中实现排队消息的等待系统

时间:2014-08-07 07:44:00

标签: c# queue

我有一个服务,它使用一个线程等待传入的消息,然后将它们放入队列。传入消息的频率各不相同。它们可以经常出现,或者有时候会以大批量发送,每批次之间的等待时间更长。

另一个线程检查队列并处理消息(如果有)。如果没有消息,那么它应该等到消息进入队列。现在,处理消息的代码如下所示。

public void Run() {
    while(thereAreNoItemsInQueue) {
        Thread.Sleep(1);
    }

    //Start processing messages from queue
}

我听说在这样的循环中使用Sleep是不好的做法,那么在这种情况下实现等待的最佳做法是什么?我已经研究了使用AutoResetEvent / ManualResetEvent或使用Monitor等待的其他方法,但我不确定哪种方法最好。

  • 我还应该补充说,这些消息被保存到的队列是一个事务性MSMQ

2 个答案:

答案 0 :(得分:2)

不在C#中。决不。参见:

  

他们可以经常来,有时他们会被大批送出   每批之间的等待时间较长。

这也可能意味着批处理的处理时间,这表示"使用持久性"。

Windows有一个队列机制(MSMQ),您可以使用数据库。但是,如果你必须处理它们,你不应该永远不会在没有持久性的情况下保留工作项。如果服务器蓝屏怎么办?

将它们转储到一个持久队列中,然后从那里开始工作。

如果必须将它们保留在队列中,...,为什么要阻塞线程?这很浪费。

当消息到来时,创建一个TASK来处理队列(如果不存在)。这样一个没有消息的队列就不会阻塞一个线程(费用很高)。

对于完全无关紧要的事情,请考虑使用自定义调度程序来设置任务(以确保它们同时处理最大x) - 瞧。它不像在进程中排队,在.NET中没有多种机制。

答案 1 :(得分:0)

您可以使用BlockingCollection

这是一些示例代码(控制台应用)。在此示例中,我使用多个工作线程和一个使用者线程,但您的代码可能只使用其中一个:

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

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

        void run()
        {
            int threadCount = 4;
            Task[] workers = new Task[threadCount];

            Task.Factory.StartNew(consumer);

            for (int i = 0; i < threadCount; ++i)
            {
                int workerId = i;
                Task task = new Task(() => worker(workerId));
                workers[i] = task;
                task.Start();
            }

            for (int i = 0; i < 100; ++i)
            {
                Console.WriteLine("Queueing work item {0}", i);
                inputQueue.Add(i);
                Thread.Sleep(50);
            }

            Console.WriteLine("Stopping adding.");
            inputQueue.CompleteAdding();
            Task.WaitAll(workers);
            outputQueue.CompleteAdding();
            Console.WriteLine("Done.");

            Console.ReadLine();
        }

        void worker(int workerId)
        {
            Console.WriteLine("Worker {0} is starting.", workerId);

            foreach (var workItem in inputQueue.GetConsumingEnumerable())
            {
                Console.WriteLine("Worker {0} is processing item {1}", workerId, workItem);
                Thread.Sleep(100);          // Simulate work.
                outputQueue.Add(workItem);  // Output completed item.
            }

            Console.WriteLine("Worker {0} is stopping.", workerId);
        }

        void consumer()
        {
            Console.WriteLine("Consumer is starting.");

            foreach (var workItem in outputQueue.GetConsumingEnumerable())
            {
                Console.WriteLine("Consumer is using item {0}", workItem);
                Thread.Sleep(25);
            }

            Console.WriteLine("Consumer is finished.");
        }

        BlockingCollection<int> inputQueue = new BlockingCollection<int>();
        BlockingCollection<int> outputQueue = new BlockingCollection<int>();
    }
}

但是,如果你正在使用.Net 4.5,你可以调查Dataflow from the the Task Parallel Library (TPL)(可能有一些更好的,更高级别的答案 - 以更陡峭的学习曲线为代价,但值得投资我想的时间。)

另见my answer in this other thread(我从中获取了上面的示例代码)。