Cleanest way to write retry logic?

前端 未结 29 2483
旧巷少年郎
旧巷少年郎 2020-11-22 03:01

Occasionally I have a need to retry an operation several times before giving up. My code is like:

int retries = 3;
while(true) {
  try {
    DoSomething();
         


        
相关标签:
29条回答
  • 2020-11-22 03:17

    Or how about doing it a bit neater....

    int retries = 3;
    while (retries > 0)
    {
      if (DoSomething())
      {
        retries = 0;
      }
      else
      {
        retries--;
      }
    }
    

    I believe throwing exceptions should generally be avoided as a mechanism unless your a passing them between boundaries (such as building a library other people can use). Why not just have the DoSomething() command return true if it was successful and false otherwise?

    EDIT: And this can be encapsulated inside a function like others have suggested as well. Only problem is if you are not writing the DoSomething() function yourself

    0 讨论(0)
  • 2020-11-22 03:20

    Implemented LBushkin's answer in the latest fashion:

        public static async Task Do(Func<Task> task, TimeSpan retryInterval, int maxAttemptCount = 3)
        {
            var exceptions = new List<Exception>();
            for (int attempted = 0; attempted < maxAttemptCount; attempted++)
            {
                try
                {
                    if (attempted > 0)
                    {
                        await Task.Delay(retryInterval);
                    }
    
                    await task();
                    return;
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }
            }
            throw new AggregateException(exceptions);
        }
    
        public static async Task<T> Do<T>(Func<Task<T>> task, TimeSpan retryInterval, int maxAttemptCount = 3)
        {
            var exceptions = new List<Exception>();
            for (int attempted = 0; attempted < maxAttemptCount; attempted++)
            {
                try
                {
                    if (attempted > 0)
                    {
                        await Task.Delay(retryInterval);
                    }
                    return await task();
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }
            }
            throw new AggregateException(exceptions);
        }  
    

    and to use it:

    await Retry.Do([TaskFunction], retryInterval, retryAttempts);
    

    whereas the function [TaskFunction] can either be Task<T> or just Task.

    0 讨论(0)
  • 2020-11-22 03:21

    Allowing for functions and retry messages

    public static T RetryMethod<T>(Func<T> method, int numRetries, int retryTimeout, Action onFailureAction)
    {
     Guard.IsNotNull(method, "method");            
     T retval = default(T);
     do
     {
       try
       {
         retval = method();
         return retval;
       }
       catch
       {
         onFailureAction();
          if (numRetries <= 0) throw; // improved to avoid silent failure
          Thread.Sleep(retryTimeout);
       }
    } while (numRetries-- > 0);
      return retval;
    }
    
    0 讨论(0)
  • 2020-11-22 03:22

    You should try Polly. It's a .NET library written by me that allows developers to express transient exception handling policies such as Retry, Retry Forever, Wait and Retry or Circuit Breaker in a fluent manner.

    Example

    Policy
        .Handle<SqlException>(ex => ex.Number == 1205)
        .Or<ArgumentException>(ex => ex.ParamName == "example")
        .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(3))
        .Execute(() => DoSomething());
    
    0 讨论(0)
  • 2020-11-22 03:22

    I'm a fan of recursion and extension methods, so here are my two cents:

    public static void InvokeWithRetries(this Action @this, ushort numberOfRetries)
    {
        try
        {
            @this();
        }
        catch
        {
            if (numberOfRetries == 0)
                throw;
    
            InvokeWithRetries(@this, --numberOfRetries);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 03:22

    Here's an async/await version that aggregates exceptions and supports cancellation.

    /// <seealso href="https://docs.microsoft.com/en-us/azure/architecture/patterns/retry"/>
    protected static async Task<T> DoWithRetry<T>( Func<Task<T>> action, CancellationToken cancelToken, int maxRetries = 3 )
    {
        var exceptions = new List<Exception>();
    
        for ( int retries = 0; !cancelToken.IsCancellationRequested; retries++ )
            try {
                return await action().ConfigureAwait( false );
            } catch ( Exception ex ) {
                exceptions.Add( ex );
    
                if ( retries < maxRetries )
                    await Task.Delay( 500, cancelToken ).ConfigureAwait( false ); //ease up a bit
                else
                    throw new AggregateException( "Retry limit reached", exceptions );
            }
    
        exceptions.Add( new OperationCanceledException( cancelToken ) );
        throw new AggregateException( "Retry loop was canceled", exceptions );
    }
    
    0 讨论(0)
提交回复
热议问题