【问题标题】:Dequeueing objects from a ConcurrentQueue in C#从 C# 中的 ConcurrentQueue 出列对象
【发布时间】:2010-08-17 13:30:04
【问题描述】:

嘿,我正在尝试在 C# 中为异步服务器实现 ConcurrentQueue。收到完整消息后,项目将立即排队。为了使消息出列,我正在制作少量线程来完成出列和服务请求的工作。这是不够的,因为每个线程都使用一个 while 循环,这会消耗相当多的处理器时间,原因很明显。

有谁知道在需要时使消息出列但不消耗太多处理时间的方法。

{
    ...

    for (int i = 0; i < 3; i++)
    {
        Thread t = new Thread(new ThreadStart(startParsingMessages));
        t.Start();
    }

    ...
}

private void startParsingMessages()
{
    QueueContainer dequeued = null;
    Console.WriteLine("Trying");
    while (true)
    {
        if (queue.TryDequeue(out dequeued))
        {
            Console.WriteLine("processing queue");
            ProcessMessage(dequeued.socket, dequeued.message);
        }
    }
}

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    您是否尝试过将其包装在BlockingCollection&lt;T&gt; 中,而不是直接使用ConcurrentQueue&lt;T&gt;?然后你可以使用TryTake(out T, TimeSpan) 等。我相信这是预期的用途:并发集合本身通常会在那里,这样你就可以选择阻塞集合的工作方式。

    当然,这不一定是这些集合的唯一用途,但特别是对于 ConcurrentQueue&lt;T&gt;,生产者/消费者队列场景是最常见的场景 - 此时 @987654326 @ 是让做正确事情变得容易的方法。

    【讨论】:

    • +1 你几乎暗示并发集合只是为了支持BlockingCollection,我不确定这是真的。但是你已经死定了,在这里使用BlockingCollection 来最小化所有的仪式,以符合传统的生产者/消费者模式。
    • @Marc:我不会说这在某种程度上是完全正确的,但我认为这将是 主要的 用途。我会更新答案。
    • 您好,感谢您的建议!我已经将我的 ConcurrentQueue 包装在 BlockingCollection 中,结果非常棒。我正在同时运行 20 个线程以通过队列吃东西,通过 TryTake(out T, TimeSpan s) 方法每个线程的阻塞时间为 0.1 秒,它的工作很有吸引力。请求在适当的时间内得到服务(比没有并发队列更快)并且 CPU 负载从未如此低。再次感谢!
    • @user352891:哇!很高兴听到:)
    • @Offler:BlockingCollection 包装了另一个IProducerConsumerCollection,如果您不指定其他任何内容,则默认为ConcurrentQueue。所以不,它不会破坏订单。
    【解决方案2】:

    并发编程中有一种众所周知的模式,称为并行生产者/消费者模式。本文可能有助于解决您的一些问题。 http://blogs.msdn.com/b/csharpfaq/archive/2010/08/12/blocking-collection-and-the-producer-consumer-problem.aspx

    【讨论】:

      【解决方案3】:

      您可以使用静态锁定对象让线程等待,然后在准备好处理某些内容时对其进行脉冲。

      static readonly object queueLock = new object();
      
      // Threads that enqueue an object
      void QueueMessage() {
        lock (queueLock) {
          queue.Enqueue(obj);
          Monitor.Pulse(queueLock);
        }
      }
      // Thread dequeuer
      private void startParsingMessages() {
        QueueContainer dequeued = null;
        Console.WriteLine("Trying");
        while (true) {
          lock(queueLock) {
            if (!queue.TryDequeue(out dequeued)) {
              Console.WriteLine("No object to dequeue, waiting...");
              // Threads will wait here and only one will be released when .Pulse()d
              Monitor.Wait(queueLock);
              dequeued = queue.Dequeue();
            }
            if (dequeued != null) {
              Console.WriteLine("processing queue");
              ProcessMessage(dequeued.socket, dequeued.message);
            }
          }
        }
      }
      

      请记住,随着您拥有的分支越多,这可能会变得相当复杂,但其要点是,您锁定一个公共对象并调用Monitor.Wait 以等待一个对象成为Pulsed。当这种情况发生时,只有一个正在等待它的线程将被释放。如果您将大量对象排入队列并希望它们全部离开,您可以调用Monitor.PulseAll,这将释放所有等待对象的线程。

      【讨论】:

      • 您好,感谢您的建议!我实施了您的建议,结果非常有希望,请求数量很少。但是,一旦有大量请求需要服务,响应时间就会急剧增加。再次感谢您的建议,这肯定是我在未来项目中会记住的一种技术。
      【解决方案4】:

      BlockingCollection 解决方案很棒。否则,在大多数情况下,简单地睡觉可能就足够了。它在响应时间上引入了一个小的延迟,并且确实消耗了少量的处理时间。优点是更强大的系统,更容易理解并且不太可能失败:

      private void startParsingMessages()
      {
          QueueContainer dequeued = null;
          Console.WriteLine("Trying");
          while (true)
          {
              //As long as there is data, process it as fast as possible.
              while(queue.TryDequeue(out dequeued))
              {
                  Console.WriteLine("processing queue");
                  ProcessMessage(dequeued.socket, dequeued.message);
              }
              //No more data, wait 100 ms before checking again
              Thread.Sleep(100);
          }
      }
      

      【讨论】:

      • 我不会称它为 BlockingCollection 更强大,但在 空闲 状态下休眠的想法是一种很好的节流模式,通常用过的。根据实时处理数据的重要性以及并发实例化这些进程的数量,您还可以引入增量退避睡眠模式,其中睡眠时间会增加每次没有处理iotems的迭代。
      • 我完全同意 BlockingCollection 在这种情况下至少同样强大。总的来说,我想展示这种模式,因为它经常出现,而且这些年来我的运气要好得多,像这样睡觉而不是某种信号机制。
      • 抱歉批评@Dwayne 我实际上几乎所有简单的基于队列的处理逻辑都喜欢这种模式,我只是在评论您使用的语言。不明显的是Sleep(100) 的魔力和一些关于如何使用它的想法。以及它如何影响事物。尤其是因为您遇到了 Jon 11 年前的帖子,这意味着我们应该更加努力地把它卖给下一位读者。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多