首页 文章

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

提问于
浏览
3

我最近在MSMQ中遇到了异步操作的问题 . 在.NET 2.0,3.0和3.5中,如果存在挂起的异步接收,并且队列被删除,则调用回调,并在调用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 / catches已经消失,因此当发生异常时,进程必须终止,因为它们在后台线程上未被捕获 .

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

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不同的排队系统,尽管到目前为止我对它很满意 .

相关问题