在正常代码流中使用异常

本文关键字:异常 代码 常代码 | 更新日期: 2023-09-27 18:13:13

我知道不应该在非异常条件下使用异常,但是我想让其他人看看我想做的事情是否真的那么糟糕。

我有一个例程,它试图从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已经超时,队列中没有可用的消息。

这个例程将在线程的循环中调用,该线程的唯一目的是检索消息。超时时间将在毫秒范围内。调用堆栈由一个方法组成,线程不负责做任何其他事情。我知道这个方法会不断抛出异常,这被认为是不好的做法,但在这种情况下,它真的那么糟糕吗?如果是这样,谁有什么建议,如何完成这个任务,而不使代码变得完全复杂?

在正常代码流中使用异常

由于您的程序逻辑(检查一个队列,如果没有消息,检查另一个队列)和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;
}

MSMQ不提供检查队列上消息计数的机制。确定消息是否可用的唯一方法是使用Peek。如果发生MessageQueueException,其错误设置为IOTimeout,则Peek已经超时,队列上没有可用的消息

你自己回答的。既然没有别的办法,你还有什么选择呢?

应该为每个轮询作业创建2个单独的线程,并让它们将消息存储在同步队列中,以便主线程可以对它们进行排序并从中获取适当的消息。这样,您就可以在Peek调用上设置"无限"超时。