2013-05-13 25 views
1

我知道你不應該爲非例外情況使用異常,但我想運行這個過去的其他程序來看看我想要做的事情真的很糟糕。在正常代碼流中使用異常

我有一個例程試圖從MSMQ消息隊列中獲取消息。如果隊列中沒有消息可用,則會檢查輔助來源以查看消息是否可用。例程如下:

void CheckForMessages(out Message msg, TimeSpan timeout) 
{ 
    try 
    { 
     queue.Peek(timeout); 
    } 
    catch(MessageQueueException ex) 
    { 
     if (e.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout) 
     { 
     if (messageAvailable) 
     { 
      msg = SecondaryMessageSource(); 
     } 

     } 

     throw; 
    } 

    msg = queue.Receive(); 
} 

MSMQ不提供檢查隊列上消息計數的機制。確定消息是否可用的唯一方法是使用Peek。如果發生MessageQueueException並將其錯誤設置爲IOTimeout,則Peek超時並且隊列上沒有消息可用。

此例程將在線程的一個循環中調用,該線程的唯一目的是檢索消息。超時將在毫秒範圍內。調用堆棧由單個方法組成,線程負責不做任何事情。我知道這種方法會不斷地拋出異常,這被認爲是不好的做法,但在這種情況下,它真的很糟糕嗎?如果是這樣,沒有任何人有任何建議,如何完成這項任務,而沒有代碼變得完全錯綜複雜?

+2

會是什麼替代使用'Peek'? – 2013-05-13 11:26:28

回答

0

MSMQ不提供用於檢查隊列上消息計數的機制。確定消息是否可用的唯一方法是使用Peek。如果發生MessageQueueException並將其錯誤設置爲IOTimeout,則Peek超時並且隊列上沒有消息可用

您自己回答了。由於沒有其他方法可以做到,你有什麼選擇?

+1

這是一個XY問題methinks。預期的解決方案只能以醜陋的方式實施,但對實際問題可能有更多解決方案。 – 2013-05-13 11:29:03

+0

他指出另一個隊列是次要來源。即故障轉移。 – jgauffin 2013-05-13 11:40:37

+0

那麼如果這意味着第二個隊列**必須不被讀取,除非第一個失敗,否則唯一正確的解決方案是異常解決方案。儘管他沒有明確指出。 – 2013-05-13 11:41:50

0

您應該爲每個輪詢作業製作2個獨立的線程,並讓它們將它們的消息存儲在同步隊列中,以便主線程可以對它們進行排序並從那裏獲取適當的線程。通過這種方式,您可以在Peek調用上設置「無限」超時。

2

由於你的程序邏輯(檢查一個隊列,如果沒有消息,檢查另一個隊列),由於MSMQ的工作方式,你將不得不處理異常。但是,我覺得可能比在catch子句中使用該代碼更優雅。

這裏是我會做:

private const int ReceiveTimeout = xxxx; 

private bool TryReceiveMessage(MessageQueue queue, out Message message) 
{ 
    try 
    { 
     message = queue.Receive(ReceiveTimeout); 

     // if we made it here, we are good and have a message 
     return true; 
    } 
    catch(MessageQueueException ex) 
    { 
     if (MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout) 
     { 
      // this is not exceptional to us, so just return false 
      return false; 
     } 

     // Throw anything else as it is unexpected 
     throw; 
    }  
} 

然後,我將你的代碼調用方法,例如:

private Message MyMethodThatIsCalledInALoop() 
{ 
    // These could also be params, etc. 
    MessageQueue primary = // whatever code to get a reference to your primary queue 
    MessageQueue secondary = // whatever code to get a reference to your secondary queue 

    Message message = null; 

    if (TryReceiveMessage(primary, out message)) 
    { 
     return message; 
    } 

    if (TryReceiveMessage(secondary, out message)) 
    { 
     return message; 
    } 

    // this would still be null 
    return message; 
}