![](/img/trans.png)
[英]Not sure if I am using polymorphism correctly and if I am can I only call methods that are in the parent
[英]Am I obliged to call EndPeek after using BeginPeek?
我有一个处理私有本地消息队列(MSMQ)的Windows服务。 当它启动时,它在队列上为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()
方法来获取包含在事务中的消息,以便在处理过程中出现错误时将消息放回队列。 再次调用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%正确的:)
顺便说一句:我可以用Message msg = q.EndPeek(asyncResult.AsyncResult)
替换Receive()
行,它同样取消我的消息,但它不会从队列中删除消息。
给这个问题一个正确的答案需要付出一些努力,因为简短的回答(“否”)可能会产生误导。
这个API的说明明确说你必须调用EndPeek()
每次调用BeginPeek()
不是我能找到的任何主题,不仅如此,它似乎在这里说明相反的情况:
要使用
BeginPeek
,请创建一个事件处理程序,用于处理异步操作的结果,并将其与事件委托相关联。BeginPeek
启动异步查看操作; 当消息到达队列时,通过引发PeekCompleted
事件通知MessageQueue
。 然后,MessageQueue
可以通过调用EndPeek(IAsyncResult)
或使用PeekCompletedEventArgs
检索结果来访问消息。
(强调我的。)这似乎说你可以使用.EndPeek()
或直接从事件args获取消息而没有义务调用.EndPeek()
。
好吧,您调用.EndPeek()
的实现任务也是如此,以使事情正常工作? 至少对于.NET 4.0中的System.Messaging
实现,答案是否定的。 当您调用.BeginPeek()
,将分配异步操作并注册回调以完成。 与此操作关联的非托管资源在此回调中被部分清除,然后才调用事件处理程序。 .EndPeek()
实际上并没有进行任何清理 - 它只是等待操作完成(如果尚未完成),检查错误并返回消息。 所以你确实可以调用.EndPeek()
或只是从事件args访问消息,或者根本不做任何事情 - 它们都可以正常工作。
糟糕,是的 - 请注意我说“部分清理”。 MessageQueue
的实现有一个问题,即它为每个异步操作分配一个ManualResetEvent
,但从不处理它,这完全取决于垃圾收集器 - 这是.NET开发人员经常被谴责的事情,当然微软自己的开发人员也没有。也不完美。 我还没有测试过这个问题中描述的OverlappedData
泄漏是否仍然具有相关性,也没有从源头上立即明显看出来,但它不会让我感到惊讶。
API有其他警告标志,它的实现可能会留下一些不足之处,最突出的是它不遵循已建立的.Begin...()
/ .End...()
模式进行异步操作,但在其中引入了事件处理程序。中间,产生一种奇怪的杂交,我从未见过其他地方。 然后有一个非常可疑的决定让Message
类继承自Component
,这给每个实例增加了相当大的开销,并提出了何时以及如何处理它的问题......总而言之,不是微软最好的工作。
现在,这一切是否意味着你没有“被迫”打电话.EndPeek()
? 是的,从某种意义上说,调用它或不调用它在资源清理或正确性方面没有任何功能差异。 但是尽管如此,我的建议仍然是调用它 。 为什么? 因为任何熟悉异步操作模式如何在其他.NET类中工作的人都希望调用存在,而不是把它放在那里看起来像一个可能导致资源泄漏的bug。 如果应用程序出现问题,这样的人可能会合理地花费一些无效的努力来查看“问题代码”。 鉴于对.EndPeek()
的调用与其他机器的开销相比可以忽略不计,我认为程序员的节省更多,而不是弥补成本。 一种可能的替代方法是插入注释,解释为什么你不调用.EndPeek()
- 但很可能这仍然需要更多的程序员周期才能掌握,而不仅仅是调用它。
从理论上讲,调用它的另一个原因是API的语义可能在将来发生变化,以便调用.EndPeek()
; 在实践中,这是不太可能发生的,因为微软传统上不愿意做出这样的重大改变(以前合理地没有调用的.EndPeek()
将停止工作)并且现有的实现已经违反了既定的做法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.