C# 在使用BeginPeek后,我是否必须给EndPeek打电话?
我有一个处理私有本地消息队列(MSMQ)的Windows服务。当它启动时,它在队列上为C# 在使用BeginPeek后,我是否必须给EndPeek打电话?,c#,msmq,peek,msmq-transaction,C#,Msmq,Peek,Msmq Transaction,我有一个处理私有本地消息队列(MSMQ)的Windows服务。当它启动时,它在队列上为PeekCompleted注册一个事件处理程序,然后调用异步BeginPeek()以等待消息到达 protected override void OnStart(string[] args) { if (String.IsNullOrWhiteSpace(args[0])) return; queue = new MessageQueue(args[0]); queue
PeekCompleted
注册一个事件处理程序,然后调用异步BeginPeek()
以等待消息到达
protected override void OnStart(string[] args)
{
if (String.IsNullOrWhiteSpace(args[0]))
return;
queue = new MessageQueue(args[0]);
queue.Formatter = new BinaryMessageFormatter();
queue.PeekCompleted += new PeekCompletedEventHandler(OnPeekCompleted);
queue.BeginPeek();
}
一旦消息到达,我的目标显然是处理该消息。我的代码当前有一个queue.Receive()
方法来获取包含在事务中的消息,以便在处理过程中出现错误时将消息放回队列<再次调用code>BeginPeek(),以重新启动循环
private static void OnPeekCompleted(Object source, PeekCompletedEventArgs asyncResult)
{
try
{
MessageQueue q = (MessageQueue)source;
using (MessageQueueTransaction trans = new MessageQueueTransaction())
{
trans.Begin();
Message msg = q.Receive(trans);
ProcessMessage(msg);
trans.Commit();
}
// Restart the asynchronous peek operation.
q.BeginPeek();
}
catch (MessageQueueException qEx)
{
// TODO: Walk it off.
}
return;
}
我是否需要在任何时候调用队列上的EndPeek()
?
也许是为了避免内存泄漏,就像暗指的那样?我很确定我不必这么做,但是文档中并不是很清楚。“开始”某件事而不“结束”它,感觉不是100%正确:)
顺便说一句:我可以将
Receive()
行替换为Message msg=q.EndPeek(asyncResult.asyncResult)
,这同样会获取消息,但不会从队列中删除消息。对这个问题给出正确的答案需要一些努力,因为简短的回答(“否”)可能会产生误导
API描述是否明确指出,每次调用BeginPeek()
都必须调用EndPeek()
?在我能找到的任何主题中都没有,而且不仅如此,它似乎说明了相反的情况:
要使用BeginPeek
,请创建一个处理结果的事件处理程序
并将其与事件关联
代表BeginPeek
启动异步窥视操作;这个
MessageQueue
通过提升peek来通知
事件,当消息到达队列时。然后可以使用消息队列
通过调用EndPeek(IAsyncResult)
或通过检索
使用PeekCompletedEventArgs
的结果
(我的重点)这似乎表明您可以使用.EndPeek()
,或者直接从事件参数获取消息,而无需调用.EndPeek()
好的,那么您调用.EndPeek()
以使事情正常工作的实现要求是什么呢?至少对于.NET 4.0中的System.Messaging
实现而言,答案是否定的。当调用.BeginPeek()
时,会分配一个异步操作并注册一个回调以完成。与此操作关联的非托管资源在此回调中被部分清除,只有在该回调中才会调用事件处理程序.EndPeek()
实际上并不进行任何清理——它只是等待操作完成(如果尚未完成),检查错误并返回消息。因此,确实可以调用.EndPeek()
,或者只从事件args访问消息,或者什么都不做——这一切都会很糟糕
很糟糕,是的——注意我说的是“部分清理”。MessageQueue
的实现有一个问题,它为每一个异步操作分配一个ManualResetEvent
,但从不处理它,这完全由垃圾收集器来完成。NET开发人员经常为此受到谴责,当然微软自己的开发人员也不是十全十美的。我没有测试中描述的重叠数据
泄漏是否仍然相关,也没有从源代码中立即发现,但这不会让我感到惊讶
API有其他警告标志,它的实现可能会留下一些需要的东西,最突出的是它不遵循已建立的<代码>。开始……(…)/代码> /<代码> .Enter……(<)/代码>模式,用于异步操作,但在中间引入事件处理程序,产生一个奇怪的混合体,我在其他任何地方都看不到。然后是让
消息
类从组件
继承的非常可疑的决定,这给每个实例增加了相当大的开销,并提出了何时以及如何处理它的问题。。。总而言之,这不是微软最好的作品
现在,所有这些是否意味着您不必“被迫”调用.EndPeek()
?是的,从某种意义上说,调用或不调用它在资源清理或正确性方面没有功能上的区别。但尽管如此,我的建议仍然是无论如何都要叫它。为什么?因为任何熟悉异步操作模式在其他.NET类中如何工作的人都会希望调用在那里,而不将其放在那里看起来像是一个可能导致资源泄漏的bug。如果应用程序有问题,这样的人可能会花费一些徒劳的努力来寻找不存在的“问题代码”。考虑到调用.EndPeek()
与机器的其他部分相比,开销可以忽略不计,我想说程序员的节省远远超过了成本。一种可能的替代方法是插入注释,解释为什么不调用.EndPeek()
——但很可能这仍然需要比调用它更多的程序员周期
理论上,调用它的另一个原因是API的语义将来可能会发生变化,从而使调用.EndPeek()
成为必要;实际上,这种情况不太可能发生,因为微软传统上不愿意做出这样的突破性更改(以前合理地没有调用.EndPeek()
的代码将停止工作)而且现有的实现已经违反了既定的实践。有没有令人信服的理由不调用.EndPeek()
?如果您不需要.EndPeek()
的结果,您可以随意放弃它,但根本不需要调用它