从多个线程获取消息到队列的最佳方法是什么,并且一次只有一个线程处理此队列中的项目?
我在尝试断开多个线程的活动时经常使用此模式。
我正在使用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()方法......
是否有其他方法可以解决这种常见模式,即让许多线程将对象添加到并发队列中 - 并且让一个线程一次一个地在项目中掠过它...
谢谢!
答案 0 :(得分:6)
最高争用线?是的,因为它是阻止集合!该调用将阻止(例如,它可能正在等待WaitHandle
),直到另一个元素被添加到集合中。
您确定这是一个问题吗?这听起来就像我期望的那样。
如果不清楚我的意思,请考虑以下代码:
var blocker = new BlockingCollection<int>();
int nextItem = blocker.Take();
您期望Take
以上呼叫运行多长时间?我希望它等待永远,因为没有任何内容被添加到blocker
。因此,如果我分析了上述代码的“性能”,我会在长时间运行的方法列表的顶部看到Take
。但这并不代表问题;再次:你希望调用阻止。
在一个完全独立的说明中,我是否可以建议将Tuple<XClientMsgExt, BOInfo, string, BOStatus>
替换为属性具有描述性名称的类型? (当然,这个建议与你的问题无关;这只是一条一般建议。)
答案 1 :(得分:2)
_q.Take()
是最高的争用线本身毫无意义。如果许多线程正在等待项目,那将会引发争用。最大的问题是,这种争论是否会使您在性能方面付出代价。您需要几个问题才能找到答案:
如果您能够保持队列不增长并且没有花费CPU资源来获取项目,那么就没有问题了。除了你可能有更多的线程而不是从集合中阅读。
答案 2 :(得分:0)
您是否考虑过使用实际的MSMQ?可能看起来有点矫枉过正,但它确实提供了你所需要的东西。不,不,你的应用程序既不能是编写器,也不能有专门的线程来读取和处理消息。