繁体   English   中英

使用BeginPeek后,我是否有义务致电EndPeek?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM