BlockingCollection - 高同步问题

时间:2011-03-18 17:59:40

标签: c# multithreading producer-consumer

从多个线程获取消息到队列的最佳方法是什么,并且一次只有一个线程处理此队列中的项目?

我在尝试断开多个线程的活动时经常使用此模式。

我正在使用BlockingCollection,如下面的代码提取中所示:

// start this task in a static constructor
Task.Factory.StartNew(() => ProcessMultiUseQueueEntries(), TaskCreationOptions.LongRunning);


private static BlockingCollection<Tuple<XClientMsgExt, BOInfo, string, BOStatus>> _q = new BlockingCollection<Tuple<XClientMsgExt, BOInfo, string, BOStatus>>();

    /// <summary>
    /// queued - Simple mechanism that will log the fact that this user is sending an xMsg (FROM a user)
    /// </summary>
    public static void LogXMsgFromUser(XClientMsgExt xMsg)
    {
        _q.Add(new Tuple<XClientMsgExt, BOInfo, string, BOStatus>(xMsg, null, "", BOStatus.Ignore));
    }

    /// <summary>
    /// queued - Simple mechanism that will log the data being executed by this user
    /// </summary>
    public static void LogBOToUser(BOInfo boInfo)
    {
        _q.Add(new Tuple<XClientMsgExt, BOInfo, string, BOStatus>(null, boInfo, "", BOStatus.Ignore));
    }

    /// <summary>
    /// queued - Simple mechanism that will log the status of the BO being executed by this user (causes the red square to flash)
    /// </summary>
    public static void LogBOStatus(string UserID, BOStatus status)
    {
        _q.Add(new Tuple<XClientMsgExt, BOInfo, string, BOStatus>(null, null, UserID, status));
    }

    /// <summary>
    /// An endless thread that will keep checking the Queue for new entrants.
    /// NOTE - no error handling since this can't fail... :) lol etc 
    /// </summary>
    private static void ProcessMultiUseQueueEntries()
    {
        while (true)        //  eternal loop
        {
            Tuple<XClientMsgExt, BOInfo, string, BOStatus> tuple = _q.Take();

            // Do stuff

        }
    }

这很好 - 所以我想 - 直到VS2010中的性能向导开始突出显示 _q.Take()行作为我代码中的最高争用行!

注意我还使用了带有ManualResetEvent组合的标准ConcurrentQueue,每次我将一个项目插入到队列中时,我发出重新发送信号,允许工作线程检查并处理队列,但这也具有突出显示的相同净效果在.WaitOne()方法......

是否有其他方法可以解决这种常见模式,即让许多线程将对象添加到并发队列中 - 并且让一个线程一次一个地在项目中掠过它...

谢谢!

3 个答案:

答案 0 :(得分:6)

最高争用线?是的,因为它是阻止集合!该调用将阻止(例如,它可能正在等待WaitHandle),直到另一个元素被添加到集合中。

您确定这是一个问题吗?这听起来就像我期望的那样。

如果不清楚我的意思,请考虑以下代码:

var blocker = new BlockingCollection<int>();
int nextItem = blocker.Take();

您期望Take以上呼叫运行多长时间?我希望它等待永远,因为没有任何内容被添加到blocker。因此,如果我分析了上述代码的“性能”,我会在长时间运行的方法列表的顶部看到Take。但这并不代表问题;再次:你希望调用阻止。


在一个完全独立的说明中,我是否可以建议将Tuple<XClientMsgExt, BOInfo, string, BOStatus>替换为属性具有描述性名称的类型? (当然,这个建议与你的问题无关;这只是一条一般建议。)

答案 1 :(得分:2)

_q.Take()是最高的争用线本身毫无意义。如果许多线程正在等待项目,那将会引发争用。最大的问题是,这种争论是否会使您在性能方面付出代价。您需要几个问题才能找到答案:

  1. 您是否能够足够快地处理项目以防止队列无限制地增长?
  2. 在CPU使用率方面,争用是否会耗费您的费用?
  3. 如果您停止向集合中添加内容,性能向导是否仍会报告争用?
  4. 如果您停止向集合中添加内容,CPU使用率是否很高?
  5. 如果您能够保持队列不增长并且没有花费CPU资源来获取项目,那么就没有问题了。除了你可能有更多的线程而不是从集合中阅读。

答案 2 :(得分:0)

您是否考虑过使用实际的MSMQ?可能看起来有点矫枉过正,但它确实提供了你所需要的东西。不,不,你的应用程序既不能是编写器,也不能有专门的线程来读取和处理消息。