2012-04-17 111 views
3

我有代碼是從MSMQ使用異步API支持讀取,即使用BeginReceive(),EndReceive()和ReceivedCompleted事件。基本模式(取自MessageQueue.ReceiveCompleted Event ...如何正確清理MSMQ偵聽器?

void StartListening() 
{ 
    _msgQ.ReceiveCompleted += ReceiveCompletedEventHandler(FooReceiveCompleted); 
    _msgQ.BeginReceive(); 
} 

void FooReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult) 
{ 
    Message msg = _msgQ.EndReceive(); 
    // Do stuff with message. 

    // Set up listening for next message. 
    _msgQ.BeginReceive(); 
} 

void StopListening() 
{ 
    _msgQ.Close(); 
} 

我可以看到的問題是,總是有未決BeginReceive()等待新的消息,並通過讀取文檔的.Net似乎沒有一個作爲一個官方/推薦的方式來清理它,以便停止收聽

如果我在沒有消息接收的情況下調用EndReceive(),那麼調用將阻塞,直到消息變爲可用爲止。 )不會清除MSMQ上的底層句柄(因此也不會清除掛起的監聽器),除非EnableConnectionCache設置爲false,否則句柄將被緩存並且不會清理乾淨在通話結束。我可以做到這一點,但理想情況下我想使用緩存。

我能看到的唯一的其他選擇是啓用緩存,然後調用靜態方法MessageQueue.ClearConnectionCache(),它大概是應用程序域範圍,因此會影響與我正在嘗試關閉的隊列無關的隊列。

附錄: 附加選項(從MessageQueue.Close())...

關閉並不總是免費的讀取和寫入手柄到隊列, ,因爲他們可能被共享。您可以採取以下任一步驟 以確保Close將讀取和寫入句柄釋放到隊列中:

創建具有獨佔訪問權限的MessageQueue。要做到這一點,調用 的MessageQueue(字符串,布爾值)或的MessageQueue(字符串,布爾值, 布爾)構造函數和sharedModeDenyReceive參數設置爲 真。

創建禁用連接高速緩存中的MessageQueue。要做到這一點, 調用的MessageQueue(字符串,布爾值,布爾)構造函數,並設置 的enableConnectionCache參數設置爲false。

禁用連接緩存。爲此,請將EnableConnectionCache 屬性設置爲false。

因此我從文檔的API的第一印象是,你不能正確地終止隊列(使用BeginReceive/EndReceive時),除非緩存不使用你必須排隊獨佔訪問。

回答

0

問題的癥結是,例如C#爲MessageQueue.ReceiveCompleted Event MSDN上使用BeginReceive()不帶參數。這是一個立即返回給調用者的異步調用,但它會導致一個可能具有很長生命週期的未完成異步操作。

當我們試圖調用Close()上的MessageQueue這位傑出的異步操作阻止的MessageQueue的適當釋放。

一個解決方案是使用BeginReceive(Timeout);這將導致ReceiveCompleted事件即使在沒有消息時也會觸發,此時我們可以測試一個標誌以查看是否正在請求關閉並允許清理正常進行。也就是說,關閉消息隊列的外部請求必須等待,例如,一個WaitHandle,ReceiveCompleted事件將發信號。因此,該模式最適合於幾秒鐘(理想情況下爲1秒或2秒)的短暫BeginReceive()超時。

+0

如何檢測到ReceivedCompleted()調用的原因是除了調用EndReceive()並捕獲超時異常之外的超時? – NTDLS 2016-10-14 16:38:48

相關問題