Поведение асинхронных исключений MSMQ - .NET 4.0 против .NET 2.0 - PullRequest
3 голосов
/ 29 ноября 2010

Недавно я столкнулся с проблемой асинхронных операций в MSMQ.В .NET 2.0, 3.0 и 3.5, если имеется ожидающее асинхронное получение, и очередь удаляется, вызывается обратный вызов, и при вызове EndReceive возникает исключение.

В .NET 4.0, обратный вызовникогда не вызывается, но исключение может быть перехвачено обработчиком события AppDomain.UnhandledException.При запуске в отладчике приложение просто завершает работу без уведомления Visual Studio о возникновении исключения.

Этот код выполняется в 64-разрядной версии Windows 7 Professional.Тем не менее, поведение одинаково, нацелено ли приложение на x86 или x64. (Редактировать: это поведение также проверялось в 32-разрядной версии XP SP3 - это ошибка платформы, не связанная с ОС)

Я предполагаю, что это новое поведение связано с.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, кажется), и охотясь через две разные сборки System.Messaging с помощью Reflector, я наконец нашел проблему.

В сборке 2.0 используются некоторые блоки try / catchв методе MessageQueue.AsynchronousRequest.RaiseCompletionEvent для перехвата исключений и сохранения кода ошибки, чтобы исключение могло быть вызвано при вызове .EndReceive ().Однако в сборке 4.0 эти попытки / перехваты пропали, поэтому при возникновении исключения процесс должен завершиться, поскольку они не перехвачены в фоновом потоке.

К сожалению, это не помогает мне решить проблему,Я рассматриваю возможность переключения на синхронный прием, но мне понравилась идея использовать для этого порты завершения ввода / вывода.

1 Ответ

2 голосов
/ 04 января 2011

Ну, я собираюсь ответить на это и принять это, так как я думаю, что это лучший ответ на ближайшее будущее.Могут пройти месяцы (или больше), прежде чем будет найдено правильное решение.

Как уже упоминалось выше, я отправил отчет об ошибке в Microsoft Connect, так что в значительной степени они должны вернуть поведение к тому, как оно работалов CLR 2.0.

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

Что касается того, как это повлияет на мое приложение, я не желаю переключаться на метод синхронного получения, поскольку он потребляет вседоступные рабочие потоки в пуле потоков.Мое приложение часто создает и удаляет много очередей, и эта проблема возникла, когда была выдана команда на удаление очереди, но ожидала ожидающая операция чтения.Вместо этого я просто отмечу, что очередь должна быть удалена, и по истечении безопасного периода времени (например, в два раза превышающего тайм-аут BeginReceive) я фактически удаляю очередь.

Или переключитесь насистема очередей, отличная от MSMQ, хотя я до сих пор доволен ею.

...