【问题标题】:How to implement re-try n times in case of exception in C#?C#出现异常时如何实现重试n次?
【发布时间】:2022-01-17 23:57:06
【问题描述】:

我正在开发一个 WPF 4.0 应用程序,我们在其中从远程 Web 服务获取数据。 Web 服务向其客户端公开了大约 120 多种方法。如果来自我的 WPF 应用程序的 Web 服务调用失败,我需要重试 n 次,这可以通过 App.Config 进行配置。如何实施?有没有解决这个问题的设计模式?

【问题讨论】:

  • 请注意,此模式不能很好地与自身组合。如果一个重试四次的函数调用了一个重试四次的函数,而该函数又调用了一个重试四次的函数,那么最后一个操作将重试 64 次。如果它在重试之间等待 30 秒,那么用户会坐在那里半小时等待错误消息。我强烈建议不要使用这种模式。当出现故障时,立即停止,告诉用户,让他们决定是重试还是去看看路由器是否拔掉了。
  • 当然对于 WPF 应用程序,重试次数不会很高!尽管如此,这对于执行后台操作的控制台应用程序非常有用。
  • 你最后做了什么?不要忘记标记答案?

标签: c# .net


【解决方案1】:
static T TryNTimes<T>(Func<T> func, int times)
{
  while (times>0)
  {
     try
     {
        return func();
     }
     catch(Exception e)
     {
       if (--times <= 0)
          throw;
     }

  }
}

【讨论】:

  • 它应该被称为 TryNTimes,而不是 ExecuteNTimes。
【解决方案2】:

我不久前写了这段代码来做一些你想做的事情。它可以根据您的需要进行修改。这是一个通用的等待方法。传入一个函数,如果没有返回预期的结果,等待然后重试并在 X 次尝试后退出。

/// <summary>
    /// Wait for the result of func to return the expeceted result
    /// </summary>
    /// <param name="func">Function to execute each cycle</param>
    /// <param name="result">Desired result returned by func</param>
    /// <param name="waitInterval">How long to wait (ms) per cycle </param>
    /// <param name="cycles">How many times to execute func before failing</param>
    /// <returns>True if desired result was attained. False if specified time runs out before desired result is returned by func</returns>
    protected static bool WaitForEvent(Func<bool> func, bool result, int waitInterval, int cycles)
    {
        int waitCount = 0;
        while (func() != result)
        {
            if (waitCount++ < cycles)
            {
                Thread.Sleep(waitInterval);
            }
            else
            {
                return false;
            }
        }

        return true;

    }

【讨论】:

  • 增加每次尝试的等待间隔可能是个好主意。
  • 如果 (waitCount++
  • 您的代码在每次尝试之间都有恒定的等待时间。通常,最好先快速重试,然后逐渐减慢。
  • 那不是解决OP的问题,就是异常重试。
  • 实际上并没有,因为您在急于复制和粘贴一些半相关的代码时忽略了包含“包装任何需要的逻辑并返回真/假...”的示例。
【解决方案3】:
while(retries < maxTries)
   try
   {
      //retryable code here
      break;
   }
   catch(Exception ex)
   {
      if(++retries == maxTries)
         throw;
      continue;
   }

当然没有什么花哨的,但它会完成工作。几乎所有实现都通用的主要模式是一些循环结构,包含并在某种程度上由 try-catch 控制;这可以是递归调用,也可以是一些迭代循环,例如上面的 while 循环。确保在成功尝试后正确退出循环,并跟踪重试;任何一个都不做都会导致无限循环。

【讨论】:

  • 如果他把它放在一个函数中,那么他就有了 120 多个方法调用的中心点。
  • 似乎没有定义任何等待间隔
  • 等待并不总是必要的;这取决于您在 try 块中到底要做什么。如果您需要确保远程计算机在尝试不成功后完成清理,请务必等待。但是,Thread.Sleep() 必须小心使用,否则会导致应用停止响应;这扩大了你必须做的事情的范围,以干净地实现这样的事情。
  • 我当然比我的 GOTO 建议更喜欢这个。 +1
【解决方案4】:
while(true)
{

try
{
 Method();
 break;
}
catch(Exception ex)
{
 i++;
 if(i == n) throw ex;
}

}

【讨论】:

    【解决方案5】:

    这是一个包含 IO 共享冲突的类似代码。相同的想法:我们有一个委托和一个包装静态方法:

    /// <summary>
    /// Defines a sharing violation wrapper delegate.
    /// </summary>
    public delegate void WrapSharingViolationsCallback();
    
    /// <summary>
    /// Wraps sharing violations that could occur on a file IO operation.
    /// </summary>
    /// <param name="action">The action to execute. May not be null.</param>
    /// <param name="exceptionsCallback">The exceptions callback. May be null.</param>
    /// <param name="retryCount">The retry count.</param>
    /// <param name="waitTime">The wait time in milliseconds.</param>
    public static void WrapSharingViolations(WrapSharingViolationsCallback action, WrapSharingViolationsExceptionsCallback exceptionsCallback, int retryCount, int waitTime)
    {
        if (action == null)
            throw new ArgumentNullException("action");
    
        for (int i = 0; i < retryCount; i++)
        {
            try
            {
                action();
                return;
            }
            catch (IOException ioe)
            {
                if ((IsSharingViolation(ioe)) && (i < (retryCount - 1)))
                {
                    bool wait = true;
                    if (exceptionsCallback != null)
                    {
                        wait = exceptionsCallback(ioe, i, retryCount, waitTime);
                    }
                    if (wait)
                    {
                        Thread.Sleep(waitTime);
                    }
                }
                else
                {
                    throw;
                }
            }
        }
    }
    

    然后,我们这样称呼它(lambda 表达式在这里非常适合):

        WrapSharingViolations(() => DoWhatever(...));
    

    【讨论】:

      【解决方案6】:

      您可以使用函数式方法:

      class Program
      {
          static T Retry<T, TException>(Func<T> thingToTry, int timesToRetry)
              where TException : Exception
          {
              // Start at 1 instead of 0 to allow for final attempt
              for (int i = 1; i < timesToRetry; i++)
              {
                  try
                  {
                      return thingToTry();
                  }
                  catch (TException)
                  {
                      // Maybe: Trace.WriteLine("Failed attempt...");
                  }
              }
      
              return thingToTry(); // Final attempt, let exception bubble up
          }
      
          static int ServiceCall()
          {
              if (DateTime.Now.Ticks % 2 == 0)
              {
                  throw new InvalidOperationException("Randomly not working");
              }
      
              return DateTime.Now.Second;
          }
      
          static void Main()
          {
              int s = Retry<int, InvalidOperationException>(ServiceCall, 10);
          }
      }
      

      您可以使用它来捕获特定异常(如果需要,添加更多 TException 泛型参数)。

      【讨论】:

      • 为什么要使用递归?那里会出现性能和内存问题,更不用说(正如您所指出的)堆栈溢出的可能性。不错的尝试,但 IMO 的设计很糟糕。
      • 好点。我最近一直在做太多的实际功能编码。固定。
      • 虽然在实际遇到任何性能和内存问题之前您必须重试很多次。
      【解决方案7】:

      你可以通过GOTO 来做到这一点(喘气)

      int count = 0;
      
      TryAgain:
      try 
      {
         // Do something with your web service
      }  
      catch(Exception e) 
      {
          if(count < numberOfAttemptsAllowed)
          {
               count++;
               goto TryAgain;
          } 
      }
      

      我确信可能有更好的方法,但这可能会满足您的需求。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-07
        • 2015-06-03
        • 2021-12-30
        • 2015-11-20
        相关资源
        最近更新 更多