C# 在使用BeginPeek后,我是否必须给EndPeek打电话?

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

我有一个处理私有本地消息队列(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()
方法来获取包含在事务中的消息,以便在处理过程中出现错误时将消息放回队列<再次调用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()
的结果,您可以随意放弃它,但根本不需要调用它