問題描述
MSMQ 異步異常行為 ‑ .NET 4.0 與 .NET 2.0 (MSMQ asynchronous exception behavior ‑ .NET 4.0 vs .NET 2.0)
我最近在 MSMQ 中遇到了異步操作的問題。在 .NET 2.0、3.0 和 3.5 中,如果存在掛起的異步接收,並且隊列被刪除,則調用回調並在調用 EndReceive 時引發異常。
在 .NET 4.0 中,回調永遠不會被調用,但異常可以被 AppDomain.UnhandledException 事件處理程序捕獲。在調試器中運行時,應用程序將簡單地終止,而不會從 Visual Studio 發出發生異常的通知。
此代碼在 64 位 Windows 7 Professional 上執行。但是,無論應用程序是針對 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 消失了,因此當發生異常時,進程必須終止,因為它們沒有被後台線程捕獲。
不幸的是,這並不能幫助我修復問題。我正在考慮切換到同步接收,
## 參考解法 #### 方法 1:
Well, I am going to answer this and accept it, since I think it's the best answer for the near future. It could be months (or more) before there is a proper solution.
As mentioned above, I filed a bug report on Microsoft Connect, so it is pretty much up to them to revert the behavior to how it worked in CLR 2.0.
Microsoft Connect: http://connect.microsoft.com/VisualStudio/feedback/details/626177/messagequeue‑beginreceive‑asynchronous‑exception‑behavior
As far as how this affects my application, I am not willing to switch to a synchronous Receive method, as that would consume all of the available worker threads on the thread pool. My application frequently creates and removes a lot of queues, and this issue arose when a command to remove a queue was issued, but an outstanding read operation was pending. Instead, I will just mark that a queue needs to be removed, and once a safe period of time has elapsed (two times the BeginReceive timeout, for instance), I will actually remove the queue.
Or switch to a different queuing system than MSMQ, though I've been happy with it so far.
(by Brad Nabholz、Brad Nabholz)