简体   繁体   English

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

[英]Am I obliged to call EndPeek after using BeginPeek?

I have a Windows service that processes a private, local message queue (MSMQ). 我有一个处理私有本地消息队列(MSMQ)的Windows服务。 When it starts, it registers an event handler for PeekCompleted on the queue, and it then calls the asynchronous BeginPeek() to wait for a message to arrive. 当它启动时,它在队列上为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();
}

Once a message arrives, my goal is to obviously process that message. 一旦消息到达,我的目标是显然处理该消息。 My code currently has a queue.Receive() method to get the message, contained within a transaction so the message gets put back on the queue in case of errors during processing. 我的代码目前有一个queue.Receive()方法来获取包含在事务中的消息,以便在处理过程中出现错误时将消息放回队列。 BeginPeek() is called again to restart the cycle. 再次调用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;
}

Do I, at any point, need to call EndPeek() on the queue ? 我在任何时候都需要在队列上调用EndPeek()吗?

Maybe to avoid memory leaks, like this question alludes to ? 也许是为了避免内存泄漏,就像这个问题所暗示的那样? I'm pretty sure I don't have to, but the documentation isn't very clear on that. 我很确定我不必这样做,但文档不是很清楚。 It just doesn't feel 100% right to 'begin' something without 'ending' it :) 如果没有'结束'它,它就不会'开始'某事是100%正确的:)

Btw: I could replace the Receive() line with Message msg = q.EndPeek(asyncResult.AsyncResult) , which equally fetches me the message, but it doesn't remove the message from the queue. 顺便说一句:我可以用Message msg = q.EndPeek(asyncResult.AsyncResult)替换Receive()行,它同样取消我的消息,但它不会从队列中删除消息。

Giving a proper answer to this question takes some effort, because the short answer ("no") could be misleading. 给这个问题一个正确的答案需要付出一些努力,因为简短的回答(“否”)可能会产生误导。

Does the API description explicitly say you must call EndPeek() for every call to BeginPeek() ? 这个API的说明明确你必须调用EndPeek()每次调用BeginPeek() Not in any topic I could find, and not only that, it appears to state the opposite here : 不是我能找到的任何主题,不仅如此,它似乎在这里说明相反的情况:

To use BeginPeek , create an event handler that processes the results of the asynchronous operation, and associate it with your event delegate. 要使用BeginPeek ,请创建一个事件处理程序,用于处理异步操作的结果,并将其与事件委托相关联。 BeginPeek initiates an asynchronous peek operation; BeginPeek启动异步查看操作; the MessageQueue is notified, through the raising of the PeekCompleted event, when a message arrives in the queue. 当消息到达队列时,通过引发PeekCompleted事件通知MessageQueue The MessageQueue can then access the message by calling EndPeek(IAsyncResult) or by retrieving the result using the PeekCompletedEventArgs . 然后, MessageQueue可以通过调用EndPeek(IAsyncResult) 使用PeekCompletedEventArgs检索结果来访问消息。

(Emphasis mine.) This seems to say that you can either use .EndPeek() or just directly get the message from the event args with no obligation to call .EndPeek() . (强调我的。)这似乎说你可以使用.EndPeek()或直接从事件args获取消息而没有义务调用.EndPeek()

Alright, so does the implementation mandate that you call .EndPeek() in order to make things work correctly? 好吧,您调用.EndPeek()的实现任务也是如此,以使事情正常工作? At least for the System.Messaging implementation in .NET 4.0, the answer is no. 至少对于.NET 4.0中的System.Messaging实现,答案是否定的。 When you call .BeginPeek() , an asynchronous operation is allocated and a callback is registered for completion. 当您调用.BeginPeek() ,将分配异步操作并注册回调以完成。 The unmanaged resources associated with this operation are partially cleaned up in this callback, and only then is the event handler called. 与此操作关联的非托管资源在此回调中被部分清除,然后才调用事件处理程序。 .EndPeek() does not actually do any cleanup -- it merely waits for the operation to complete if it hasn't yet, checks for errors, and returns the message. .EndPeek()实际上并没有进行任何清理 - 它只是等待操作完成(如果尚未完成),检查错误并返回消息。 So it is indeed true that you can either call .EndPeek() or just access the message from the event args, or do nothing at all -- it will all work just as poorly. 所以你确实可以调用.EndPeek()或只是从事件args访问消息,或者根本不做任何事情 - 它们都可以正常工作。

Poorly, yes -- note that I said "partially cleaned up". 糟糕,是的 - 请注意我说“部分清理”。 The implementation of MessageQueue has a problem in that it allocates a ManualResetEvent for every asynchronous operation, but never disposes it, leaving this entirely up to the garbage collector -- something .NET developers are often excoriated for doing, but of course Microsoft's own developers aren't perfect either. MessageQueue的实现有一个问题,即它为每个异步操作分配一个ManualResetEvent ,但从不处理它,这完全取决于垃圾收集器 - 这是.NET开发人员经常被谴责的事情,当然微软自己的开发人员也没有。也不完美。 I haven't tested whether the OverlappedData leak described in this question is still relevant, nor is it immediately obvious from the source, but it would not surprise me. 我还没有测试过这个问题中描述的OverlappedData泄漏是否仍然具有相关性,也没有从源头上立即明显看出来,但它不会让我感到惊讶。

The API has other warning signs that its implementation may leave something to be desired, most prominently that it does not follow the established .Begin...() / .End...() pattern for asynchronous operations but introduces event handlers in the middle, producing a strange hybrid I've never see anywhere else. API有其他警告标志,它的实现可能会留下一些不足之处,最突出的是它不遵循已建立的.Begin...() / .End...()模式进行异步操作,但在其中引入了事件处理程序。中间,产生一种奇怪的杂交,我从未见过其他地方。 Then there's the very dubious decision of making the Message class inherit from Component , which adds considerable overhead to every instance and raises the question of when and how it should be disposed... all in all, not Microsoft's best work. 然后有一个非常可疑的决定让Message类继承自Component ,这给每个实例增加了相当大的开销,并提出了何时以及如何处理它的问题......总而言之,不是微软最好的工作。

Now, does all this mean you're not "obliged" to call .EndPeek() ? 现在,这一切是否意味着你没有“被迫”打电话.EndPeek() Yes, in the sense that calling it or not calling it makes no functional difference with regards to resource cleanup or correctness. 是的,从某种意义上说,调用它或不调用它在资源清理或正确性方面没有任何功能差异。 But with all that said, my advice is still to call it anyway . 但是尽管如此,我的建议仍然是调用它 Why? 为什么? Because anyone who is familiar with how the asynchronous operation pattern works in other .NET classes would expect the call to be there, and not putting it there looks like a bug that could lead to resource leaks. 因为任何熟悉异步操作模式如何在其他.NET类中工作的人都希望调用存在,而不是把它放在那里看起来像一个可能导致资源泄漏的bug。 If there is a problem with the application, such a person might reasonably spend some fruitless effort looking at "problem code" that isn't. 如果应用程序出现问题,这样的人可能会合理地花费一些无效的努力来查看“问题代码”。 Given that a call to .EndPeek() has negligible overhead compared to the rest of the machinery, I'd say the savings in programmer surprise more than make up for the costs. 鉴于对.EndPeek()的调用与其他机器的开销相比可以忽略不计,我认为程序员的节省更多,而不是弥补成本。 A possible alternative is to insert a comment instead, explaining why you're not calling .EndPeek() -- but in all probability this still takes more programmer cycles to grasp than just calling it. 一种可能的替代方法是插入注释,解释为什么你不调用.EndPeek() - 但很可能这仍然需要更多的程序员周期才能掌握,而不仅仅是调用它。

In theory, another reason for calling it is that the semantics of the API could change in the future to make the call to .EndPeek() necessary; 从理论上讲,调用它的另一个原因是API的语义可能在将来发生变化,以便调用.EndPeek() ; in practice, this is very unlikely to happen because Microsoft is traditionally reluctant to make breaking changes like this (code that previously and reasonably did not call .EndPeek() would stop working) and the existing implementation already contravenes established practice. 在实践中,这是不太可能发生的,因为微软传统上不愿意做出这样的重大改变(以前合理地没有调用的.EndPeek()将停止工作)并且现有的实现已经违反了既定的做法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 不确定我是否正确使用多态,如果我可以,我只能调用父级中的方法 - Not sure if I am using polymorphism correctly and if I am can I only call methods that are in the parent 用完表格后需要处理吗? - Do i need to dispose a form after I am done using it? 我正在尝试使用带有IronPython nuget的C#调用python - I am trying to call python using C# with the IronPython nuget 为什么我在 ajax post 调用后得到 json 响应页面? - Why am I getting json response page after ajax post call? 使用完毕后,无法删除已保存的图像文件 - Cannot delete a saved image file after I am done using it 为什么我在使用反射后在 .NET 7 中出现转换错误? - Why am I getting a casting error in .NET 7 after using reflection? 如果我必须使用与物理列相同的名称,ColumnMappings的用途是什么? - What is the use of ColumnMappings if I'm obliged to use the same name as the physical column? 致电WebBrowser.Refresh之后,我应该等待多长时间才能执行某项操作? - How long am I supposed to wait to do something after I call WebBrowser.Refresh? 我试图在循环中调用一个方法。它应该在 10 秒内只调用 20 次。 我正在使用如下代码所示的信号量 - I am trying to call a method in a loop .It should be called only 20 times in 10 seconds . I am using semaphore like the below code 我应该在哪里使用C#调用方法connection.Open()? - Where am I supposed to call the method connection.Open() using C#?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM