有时,我需要在放弃之前将一个操作重试几次。我的代码是:

int retries = 3;
while(true) {
  try {
    DoSomething();
    break; // success!
  } catch {
    if(--retries == 0) throw;
    else Thread.Sleep(1000);
  }
}

我想在一个通用的重试函数中重写这个:

TryThreeTimes(DoSomething);

这在c#中可行吗?TryThreeTimes()方法的代码是什么?


当前回答

对于那些既想对任何异常进行重试,又想显式设置异常类型的人,可以使用以下方法:

public class RetryManager 
{
    public void Do(Action action, 
                    TimeSpan interval, 
                    int retries = 3)
    {
        Try<object, Exception>(() => {
            action();
            return null;
        }, interval, retries);
    }

    public T Do<T>(Func<T> action, 
                    TimeSpan interval, 
                    int retries = 3)
    {
        return Try<T, Exception>(
              action
            , interval
            , retries);
    }

    public T Do<E, T>(Func<T> action, 
                       TimeSpan interval, 
                       int retries = 3) where E : Exception
    {
        return Try<T, E>(
              action
            , interval
            , retries);
    }

    public void Do<E>(Action action, 
                       TimeSpan interval, 
                       int retries = 3) where E : Exception
    {
        Try<object, E>(() => {
            action();
            return null;
        }, interval, retries);
    }

    private T Try<T, E>(Func<T> action, 
                       TimeSpan interval, 
                       int retries = 3) where E : Exception
    {
        var exceptions = new List<E>();

        for (int retry = 0; retry < retries; retry++)
        {
            try
            {
                if (retry > 0)
                    Thread.Sleep(interval);
                return action();
            }
            catch (E ex)
            {
                exceptions.Add(ex);
            }
        }

        throw new AggregateException(exceptions);
    }
}

其他回答

对于那些既想对任何异常进行重试,又想显式设置异常类型的人,可以使用以下方法:

public class RetryManager 
{
    public void Do(Action action, 
                    TimeSpan interval, 
                    int retries = 3)
    {
        Try<object, Exception>(() => {
            action();
            return null;
        }, interval, retries);
    }

    public T Do<T>(Func<T> action, 
                    TimeSpan interval, 
                    int retries = 3)
    {
        return Try<T, Exception>(
              action
            , interval
            , retries);
    }

    public T Do<E, T>(Func<T> action, 
                       TimeSpan interval, 
                       int retries = 3) where E : Exception
    {
        return Try<T, E>(
              action
            , interval
            , retries);
    }

    public void Do<E>(Action action, 
                       TimeSpan interval, 
                       int retries = 3) where E : Exception
    {
        Try<object, E>(() => {
            action();
            return null;
        }, interval, retries);
    }

    private T Try<T, E>(Func<T> action, 
                       TimeSpan interval, 
                       int retries = 3) where E : Exception
    {
        var exceptions = new List<E>();

        for (int retry = 0; retry < retries; retry++)
        {
            try
            {
                if (retry > 0)
                    Thread.Sleep(interval);
                return action();
            }
            catch (E ex)
            {
                exceptions.Add(ex);
            }
        }

        throw new AggregateException(exceptions);
    }
}

这可能是个坏主意。首先,这是一句格言的象征:“疯狂的定义是做同一件事两次,每次都期待不同的结果”。其次,这种编码模式本身不能很好地组合。例如:

假设您的网络硬件层在失败时重发一个数据包三次,在两次失败之间等待一秒钟。

现在假设软件层在包失败时重发关于失败的通知三次。

现在假设通知层在通知传递失败时重新激活通知三次。

现在假设错误报告层在通知失败时重新激活通知层三次。

现在假设web服务器在错误失败时重新激活错误报告三次。

现在假设web客户端在从服务器得到错误后重新发送请求三次。

现在假设网络交换机上用来将通知路由到管理员的线路被拔掉。web客户端的用户什么时候最终得到错误消息?我大约12分钟后到。

以免您认为这只是一个愚蠢的例子:我们已经在客户代码中看到了这个错误,尽管比我在这里描述的要严重得多。在特定的客户代码中,错误条件发生和最终报告给用户之间的间隔是几个星期,因为有很多层自动重试并等待。想象一下,如果是十次而不是三次,会发生什么。

通常正确的处理错误的方法是立即报告它,让用户决定怎么做。如果用户想要创建自动重试的策略,让他们在软件抽象的适当级别上创建该策略。

使用c# 6.0保持简单

public async Task<T> Retry<T>(Func<T> action, TimeSpan retryInterval, int retryCount)
{
    try
    {
        return action();
    }
    catch when (retryCount != 0)
    {
        await Task.Delay(retryInterval);
        return await Retry(action, retryInterval, --retryCount);
    }
}
public delegate void ThingToTryDeletage();

public static void TryNTimes(ThingToTryDelegate, int N, int sleepTime)
{
   while(true)
   {
      try
      {
        ThingToTryDelegate();
      } catch {

            if( --N == 0) throw;
          else Thread.Sleep(time);          
      }
}

瞬态故障处理应用程序块提供了可扩展的重试策略集合,包括:

增量 固定间隔 指数退下

它还包括一组用于基于云的服务的错误检测策略。

有关更多信息,请参阅开发人员指南的本章。

可通过NuGet(搜索'topaz')。