MSMQ异步异常行为 - .NET 4.0与.NET 2.0

3
我最近在 MSMQ 中遇到了一个异步操作问题。在 .NET 2.0、3.0 和 3.5 中,如果有一个 pending 的异步接收任务,并且队列被删除,回调函数会被调用,而在调用 EndReceive 时会抛出异常。
在 .NET 4.0 中,回调函数不会被调用,但是异常可以被 AppDomain.UnhandledException 事件处理程序捕获。当在调试器中运行时,应用程序将简单终止,Visual Studio 不提供任何有关异常发生的通知。
这段代码在 Windows 7 Professional 64 位上执行,但无论应用程序针对 x86 还是 x64,行为都相同。(编辑:在 XP SP3 32 位上验证了此行为,这似乎是一个框架错误,而不是与操作系统相关)
我假设这种新的行为与 .NET 4.0 是完全新的运行时有关。我现在不确定该怎么办,但基本上我希望能够恢复到 .NET 4.0 之前的行为,同时仍然针对 .NET 4.0 运行时。如果您能提供任何帮助或建议,我将非常感激。以下是重现此问题的示例代码:
class Program
{
    static void Main( string[] args )
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler( CurrentDomain_UnhandledException );
        string path = @".\private$\mytestqueue";
        // Create queue only if it doesn't already exist.
        var queue = MessageQueue.Exists( path ) ? new MessageQueue( path ) : MessageQueue.Create( path );
        queue.BeginReceive( TimeSpan.FromSeconds( 15 ), queue, new AsyncCallback( ReceiveComplete ) );
        Thread.Sleep( 5000 );
        MessageQueue.Delete( path );
    }

    static void CurrentDomain_UnhandledException( object sender, UnhandledExceptionEventArgs e )
    {
        var mqEx = (MessageQueueException) e.ExceptionObject;

        // .NET 4.0:

        // "The queue does not exist or you do not have sufficient
        // permissions to perform the operation."
        Console.WriteLine( mqEx.Message );
        // "QueueNotFound"
        Console.WriteLine( mqEx.MessageQueueErrorCode );
    }

    static void ReceiveComplete( IAsyncResult ar )
    {
        // This callback is never invoked under .NET 4.0.
        Console.WriteLine( "Finishing Receive." );
        var queue = (MessageQueue) ar.AsyncState;
        try
        {
            queue.EndReceive( ar );
        }
        catch ( MessageQueueException mqEx )
        {
            // .NET 2.0 through 3.5:

            // "Queue handle can no longer be used to receive messages
            // because the queue was deleted. The handle should be closed."
            Console.WriteLine( mqEx.Message );
            // "QueueDeleted"
            Console.WriteLine( mqEx.MessageQueueErrorCode );
        }
    }
}

附言:

花费了太多时间尝试使用源代码步进(似乎System.Messaging源代码适用于4.0但不适用于2.0/3.5),并且在Reflector中搜索了两个不同的System.Messaging程序集,最终我找到了问题所在。

在2.0程序集中,MessageQueue.AsynchronousRequest.RaiseCompletionEvent方法中使用了一些try/catch块来捕获异常并存储错误代码,以便在调用.EndReceive()时引发异常。 但是,在4.0程序集中,这些try/catch已经不存在了,因此当发生异常时,进程必须终止,因为它们在后台线程中未被捕获。

不幸的是,这并没有帮助我解决问题。 我正在考虑切换到同步接收,但我喜欢利用I/O完成端口的想法。


2
你说服了我,将此发布到connect.microsoft.com。 - Hans Passant
@Hans:太好了,我之前应该想到这个办法!如果有人遇到同样的问题,这里是连接错误反馈:http://connect.microsoft.com/VisualStudio/feedback/details/626177/messagequeue-beginreceive-asynchronous-exception-behavior - Brad Nabholz
现在我只是在导航到Microsoft Connect网站时收到“页面未找到”的错误。有人对此错误的状态有任何信息吗? - DeCaf
巧合的是,我几天前查看了这个问题并在Connect上看到了它。我现在在我的机器上运行.NET 4.5.2,我的上面的测试代码无论针对哪个框架版本都可以正常工作;此外,如果我使用ILSpy,我会在System.Messaging.dll的2.0和4.0版本的RaiseCompletionEvent方法中看到相同的代码。所以它似乎已经被修复了! - Brad Nabholz
1个回答

2

好的,我将回答并接受它,因为我认为这是近期最好的答案。在有一个合适的解决方案之前可能需要数月时间。

如上所述,我在Microsoft Connect上提交了一个错误报告,因此重置行为以使其与CLR 2.0中的工作方式相同基本上取决于他们。

Microsoft Connect:http://connect.microsoft.com/VisualStudio/feedback/details/626177/messagequeue-beginreceive-asynchronous-exception-behavior

就我的应用程序受到的影响而言,我不愿切换到同步的Receive方法,因为那样会消耗线程池中所有可用的工作线程。我的应用程序经常创建和删除大量队列,当发出删除队列的命令但挂起了尚未完成读操作时,就会出现这个问题。相反,我只会标记需要删除队列,并在安全时间段过去后(例如两倍于BeginReceive超时的时间),我才会实际删除队列。

或者切换到与MSMQ不同的排队系统,尽管我迄今为止还很满意它。


2
你为什么经常需要创建和删除大量的队列? - Giorgi Chakhidze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接