【问题标题】:Repeating a function in C# until it no longer throws an exception在 C# 中重复一个函数,直到它不再引发异常
【发布时间】:2011-12-16 18:22:39
【问题描述】:

我有一个调用 SOAP 接口的类,并返回一组数据。但是,如果此请求超时,则会引发异常。这很好。但是,我希望我的程序尝试再次进行此调用。如果超时,我希望它继续进行此调用,直到它成功。我怎样才能做到这一点?

例如:

try
{
   salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
   ?? What Goes Here to FORCE the above line of code to rerun until it succeeds.
}

【问题讨论】:

  • 增加超时不抛出异常?
  • 您可以简单地从 catch 块中再次调用该方法,BUT - 如果服务长时间停机会发生什么?您是否希望该方法递归运行 24 小时?我建议将其限制为一定的重试次数。
  • 在所有这些答案中,没有一个人告诉您要捕获更具体的异常。如果您的 try 块包含处于无效状态的对象以获取 salesOrderList 怎么办?重试永远不会成功。

标签: c# exception


【解决方案1】:

如果您无法更改超时,以下应该可以工作。 salesOrdersArray 应初始化为null

while(salesOrdersArray == null)
{
    try
    {
       salesOrdersArray = MagServ.salesOrderList(sessID, filter);
    }
    catch
    {
       // Log failure
    }
}

【讨论】:

  • +1:我比 Jon 的回答更喜欢这个。我认为它使循环的结束条件更加明显。
【解决方案2】:

使用异常作为控制流通常不是一个好主意,但这会满足您的要求。

bool Caught = true;
while (Caught)
try
{
    salesOrdersArray = MagServ.salesOrderList(sessID, filter);
    Caught = false;
}
catch
{
    Caught = true;
}

【讨论】:

    【解决方案3】:
    bool repeat = true;
    while (repeat)
    {
        try
        {
           salesOrdersArray = MagServ.salesOrderList(sessID, filter);
           repeat = false;
        }
        catch
        {
        }
    }
    

    【讨论】:

      【解决方案4】:

      你只需要永远循环:

      while (true)
      {
          try
          {
              salesOrdersArray = MagServ.salesOrderList(sessID, filter);
              break; // Exit the loop. Could return from the method, depending
                     // on what it does...
          }
          catch
          {
              // Log, I suspect...
          }
      }
      

      请注意,您几乎可以肯定实际上永远循环。您几乎可以肯定有最大尝试次数,并且可能只捕获特定异常。捕获所有异常永远可能会令人震惊......想象一下如果salesOrderList(非常规方法名称,顺便说一句)抛出ArgumentNullException,因为你有一个错误和@987654324 @ 为空...您真的想永远占用 100% 的 CPU 吗?

      【讨论】:

      • 不能(可能)占用系统资源和/或使调用应用程序无响应吗?听起来对我来说不是最好的解决方案......
      • @Tim:我在下面添加了一段 - 基本上代码直接回答了问题,但该段解释说这可能不是一个好主意:)
      • +1 表示特定例外情况,我在您编辑期间输入了我对问题的评论。
      • 我知道,我确实捕获了特定的异常、SOAP 错误等。忘记了休息的乐趣,谢谢。
      • @psyklopz:您在catch块中指定异常类型:catch (SoapException)等。您可以在多个catch块中指定多种不同的异常类型。
      【解决方案5】:

      试试

      bool failed = false;
      do {
       try
       {
        salesOrdersArray = MagServ.salesOrderList(sessID, filter);
       }
       catch
       {
        failed = true;
       }
      } while(failed);
      

      如果这永远不会成功,您所追求的行为可能会导致无限循环......

      【讨论】:

        【解决方案6】:

        试试这样的:

        var failed = true;
        while (failed)
        {
          try 
          {
            salesOrdersArray = MagServ.salesOrderList(sessID, filter); 
            failed = false;
          }
          catch
          {
          }
        }
        

        编辑:哇!英雄所见略同! :)

        【讨论】:

          【解决方案7】:

          虽然我不建议你无限次这样做,但你可以用那句话制作一个单独的函数:

          void GoConnect()
          {
              try
              {
                  salesOrdersArray = MagServ.salesOrderList(sessID, filter);
              }
              catch
              {
                  GoConnect();
              }
          }
          

          【讨论】:

            【解决方案8】:
            while(salesOrdersArray == null){
            
              try
              {
                salesOrdersArray = MagServ.salesOrderList(sessID, filter);
              }
              catch(salesOrderException e)
              {
                 log(e.message);
              }
            }
            

            这将永远运行,并且使用异常作为一个缓慢的循环。有没有办法可以修改返回 null 而不是抛出异常的函数?如果您预计此调用会定期失败,请不要使用 try/catch 块。

            【讨论】:

              【解决方案9】:

              我遵循这个模式来解决这个问题:

                  public void Send(String data, Int32 attemptNumber)
                  {
                      try
                      {
                          yourCodeHere(data);
                      }
                      catch (WebException ex)
                      {
                          if (attemptNumber > 0)
                              Send(data, --attemptNumber);
                          else
                              throw new AttemptNumberExceededException("Attempt number exceeded!", ex);
                      }
                      catch (Exception ex)
                      {
                          //Log pourpose code goes here!
                          throw;
                      }
                  }
              

              永远尝试似乎不是一个好主意,因为您最终可能会有一个无限的过程。如果您认为需要多次尝试才能实现目标,请在此处设置大量数字。

              我个人认为在调用 Send(data); 之前等待几毫秒或 eac 尝试 Thread.Sleep(1000); 几秒是明智的——你例如,可以使用 attempNumber 变量来增加或减少此等待时间,如果您认为这对您的方案是明智的。

              【讨论】:

              • 我为什么不应该?我的观点和 Jon Skeet 一样,在他写的代码下面
              • 感谢@asawyer 不知道,所以我编辑了我的答案!现在可以了吗?
              • 两件事: 1- 你几乎从不想捕获基异常类,如果你这样做,它几乎只是为了记录目的,然后throwing 再次。 2 - 永远不要抛出基础Exception 类。您正在强制使用代码来捕获Exception 的不良做法。使用更合适的子类,或创建自己的子类。
              • 这很有意义......!从来没有这样想过……抱歉让你厌烦了,现在呢?
              【解决方案10】:

              您必须将 try/catch 块放在循环结构中。如果您不想消耗 100% 的处理器,请在 catch 块中放置一个 Thread.Sleep,这样每次发生异常时,它都会等待一段时间,从而释放处理器来做其他事情。

              // iterate 100 times... not forever!
              for (int i = 0; i < 100; i++)
              {
                  try {
                      // do your work here;
              
                      break; // break the loop if everything is fine
                  } catch {
                      Thread.Sleep(1000);
                  }
              }
              

              也可以指定异常类型,只处理超时异常,其他类型的异常直通。

              // iterate 100 times... not forever!
              for (int i = 0; i < 100; i++)
              {
                  try {
                      // do your work here;
              
                      break; // break the loop if everything is fine
                  } catch (TimeOutException) {
                      Thread.Sleep(1000);
                  }
              }
              

              注意,TimeOutException 应该换成异常的真名……不知道是不是真名。

              还要调整睡眠时间(以毫秒为单位)和重复次数,在我介绍的情况下,100 次 1000 毫秒的重复产生的最长等待时间为 1 分 40 秒,加上操作时间本身。

              【讨论】:

                【解决方案11】:

                我将使用 事务性 队列 (MSMQ) 来存储服务调用。一个循环将使消息出列并在 TransactionScope 中调用服务,如果调用失败,则消息似乎仍在队列中。可以通过在消息中添加过期时间来指定总体超时。如果你真的想要一个可靠的解决方案,这个解决方案很好,因为我猜调用该操作很关键。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2019-05-19
                  • 2019-12-29
                  • 1970-01-01
                  • 2020-11-23
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-09-21
                  相关资源
                  最近更新 更多