Внедрение автоматического перезапуска асинхронного кода [дубликат]

Еще один ответ, который легко отслеживать и довольно эффективен для операции O (n) без использования небезопасного кода или копирования частей исходных массивов.

Обязательно проверьте. Некоторые из предложений, найденных в этой теме, восприимчивы к ситуациям.

    static void Main(string[] args)
    {
        //                                                         1   1  1  1  1  1  1  1  1  1  2   2   2
        //                           0  1  2  3  4  5  6  7  8  9  0   1  2  3  4  5  6  7  8  9  0   1   2  3  4  5  6  7  8  9
        byte[] buffer = new byte[] { 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 5, 5, 0, 5, 5, 1, 2 };
        byte[] beginPattern = new byte[] { 1, 0, 2 };
        byte[] middlePattern = new byte[] { 8, 9, 10 };
        byte[] endPattern = new byte[] { 9, 10, 11 };
        byte[] wholePattern = new byte[] { 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
        byte[] noMatchPattern = new byte[] { 7, 7, 7 };

        int beginIndex = ByteArrayPatternIndex(buffer, beginPattern);
        int middleIndex = ByteArrayPatternIndex(buffer, middlePattern);
        int endIndex = ByteArrayPatternIndex(buffer, endPattern);
        int wholeIndex = ByteArrayPatternIndex(buffer, wholePattern);
        int noMatchIndex = ByteArrayPatternIndex(buffer, noMatchPattern);
    }

    /// <summary>
    /// Returns the index of the first occurrence of a byte array within another byte array
    /// </summary>
    /// <param name="buffer">The byte array to be searched</param>
    /// <param name="pattern">The byte array that contains the pattern to be found</param>
    /// <returns>If buffer contains pattern then the index of the first occurrence of pattern within buffer; otherwise, -1</returns>
    public static int ByteArrayPatternIndex(byte[] buffer, byte[] pattern)
    {
        if (buffer != null && pattern != null && pattern.Length <= buffer.Length)
        {
            int resumeIndex;
            for (int i = 0; i <= buffer.Length - pattern.Length; i++)
            {
                if (buffer[i] == pattern[0]) // Current byte equals first byte of pattern
                {
                    resumeIndex = 0;
                    for (int x = 1; x < pattern.Length; x++)
                    {
                        if (buffer[i + x] == pattern[x])
                        {
                            if (x == pattern.Length - 1)  // Matched the entire pattern
                                return i;
                            else if (resumeIndex == 0 && buffer[i + x] == pattern[0])  // The current byte equals the first byte of the pattern so start here on the next outer loop iteration
                                resumeIndex = i + x;
                        }
                        else
                        {
                            if (resumeIndex > 0)
                                i = resumeIndex - 1;  // The outer loop iterator will increment so subtract one
                            else if (x > 1)
                                i += (x - 1);  // Advance the outer loop variable since we already checked these bytes
                            break;
                        }
                    }
                }
            }
        }
        return -1;
    }

    /// <summary>
    /// Returns the indexes of each occurrence of a byte array within another byte array
    /// </summary>
    /// <param name="buffer">The byte array to be searched</param>
    /// <param name="pattern">The byte array that contains the pattern to be found</param>
    /// <returns>If buffer contains pattern then the indexes of the occurrences of pattern within buffer; otherwise, null</returns>
    /// <remarks>A single byte in the buffer array can only be part of one match.  For example, if searching for 1,2,1 in 1,2,1,2,1 only zero would be returned.</remarks>
    public static int[] ByteArrayPatternIndex(byte[] buffer, byte[] pattern)
    {
        if (buffer != null && pattern != null && pattern.Length <= buffer.Length)
        {
            List<int> indexes = new List<int>();
            int resumeIndex;
            for (int i = 0; i <= buffer.Length - pattern.Length; i++)
            {
                if (buffer[i] == pattern[0]) // Current byte equals first byte of pattern
                {
                    resumeIndex = 0;
                    for (int x = 1; x < pattern.Length; x++)
                    {
                        if (buffer[i + x] == pattern[x])
                        {
                            if (x == pattern.Length - 1)  // Matched the entire pattern
                                indexes.Add(i);
                            else if (resumeIndex == 0 && buffer[i + x] == pattern[0])  // The current byte equals the first byte of the pattern so start here on the next outer loop iteration
                                resumeIndex = i + x;
                        }
                        else
                        {
                            if (resumeIndex > 0)
                                i = resumeIndex - 1;  // The outer loop iterator will increment so subtract one
                            else if (x > 1)
                                i += (x - 1);  // Advance the outer loop variable since we already checked these bytes
                            break;
                        }
                    }
                }
            }
            if (indexes.Count > 0)
                return indexes.ToArray();
        }
        return null;
    }
385
задан abatishchev 20 May 2016 в 18:06
поделиться

25 ответов

Операторы выписки бланкета, которые просто повторяют один и тот же вызов, могут быть опасными, если они используются как общий механизм обработки исключений. Сказав это, вот обертка повторных попыток на основе лямбда, которую вы можете использовать любым способом. Я выбрал фактор количества повторений и тайм-аут повтора в качестве параметров для большей гибкости:

public static class Retry
{
    public static void Do(
        Action action,
        TimeSpan retryInterval,
        int maxAttemptCount = 3)
    {
        Do<object>(() =>
        {
            action();
            return null;
        }, retryInterval, maxAttemptCount);
    }

    public static T Do<T>(
        Func<T> action,
        TimeSpan retryInterval,
        int maxAttemptCount = 3)
    {
        var exceptions = new List<Exception>();

        for (int attempted = 0; attempted < maxAttemptCount; attempted++)
        {
            try
            {
                if (attempted > 0)
                {
                    Thread.Sleep(retryInterval);
                }
                return action();
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
        }
        throw new AggregateException(exceptions);
    }
}

Теперь вы можете использовать этот метод утилиты для выполнения логики повтора:

Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));

или:

Retry.Do(SomeFunctionThatCanFail, TimeSpan.FromSeconds(1));

или:

int result = Retry.Do(SomeFunctionWhichReturnsInt, TimeSpan.FromSeconds(1), 4);

Или вы даже можете сделать перегрузку async.

476
ответ дан VCD 19 August 2018 в 00:39
поделиться
  • 1
    +1, особенно для предупреждения и проверки ошибок. Мне было бы более комфортно, если бы это прошло в типе исключения, чтобы поймать как общий параметр (где T: Exception). – TrueWill 13 October 2009 в 23:55
  • 2
    Мое намерение заключалось в том, что «повторы» на самом деле означало повторные попытки. Но это не так сложно изменить, чтобы означать «пытается». Пока имя сохраняется значимым. Существуют и другие возможности для улучшения кода, например, проверка отрицательных попыток или отрицательные таймауты - например. Я пропустил их в основном, чтобы простой пример ... но опять же, на практике это, вероятно, будет хорошим дополнением к реализации. – LBushkin 14 October 2009 в 01:05
  • 3
    Мы используем аналогичный шаблон для нашего доступа к базе данных в приложении Biztalk с большим объемом, но с двумя улучшениями: у нас есть черные списки для исключений, которые не следует повторять, и мы сохраняем первое исключение, которое возникает, и бросаем это, когда попытка повторения в конечном итоге терпит неудачу. Причина в том, что второе и последующие исключения часто отличаются от второго. В этом случае вы скрываете исходную проблему при переустановке только последнего исключения. – TToni 22 March 2012 в 13:21
  • 4
    @Dexters Выбрасываем новое исключение из исходного исключения как внутреннего исключения. Исходная трассировка стека доступна как атрибут из внутренних исключений. – TToni 31 March 2013 в 22:31
  • 5
    Вы также можете попробовать использовать библиотеку с открытым исходным кодом, такую ​​как Polly , чтобы справиться с этим. Существует гораздо больше гибкости для ожидания между попытками, и это подтверждается многими другими, которые использовали проект. Пример: Policy.Handle<DivideByZeroException>().WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) }); – Todd Meinershagen 25 August 2015 в 02:28

Я знаю, что этот ответ очень старый, но я просто хотел прокомментировать это, потому что я столкнулся с проблемами, используя их, делая, независимо от утверждения с помощью счетчиков.

На протяжении многих лет я думаю о лучшем подходе. То есть использовать какую-то агрегацию событий, например, реактивные расширения «Тема» или тому подобное. Когда попытка не выполняется, вы просто публикуете событие, в котором говорится, что попытка не выполнена, и функция агрегатора перераспределяет событие. Это позволяет вам намного больше контролировать повторение, не загрязняя сам вызов кучей циклов повтора, а что нет. Также вы не связываете нить с кучей нитей.

0
ответ дан Brandon 19 August 2018 в 00:39
поделиться

Разрешение функций и повторных сообщений

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;
}
15
ответ дан Brian 19 August 2018 в 00:39
поделиться

Сделать это просто на C #, Java или других языках:

  internal class ShouldRetryHandler {
    private static int RETRIES_MAX_NUMBER = 3;
    private static int numberTryes;

    public static bool shouldRetry() {
        var statusRetry = false;

        if (numberTryes< RETRIES_MAX_NUMBER) {
            numberTryes++;
            statusRetry = true;
            //log msg -> 'retry number' + numberTryes

        }

        else {
            statusRetry = false;
            //log msg -> 'reached retry number limit' 
        }

        return statusRetry;
    }
}

и использовать его в коде очень просто:

 void simpleMethod(){
    //some code

    if(ShouldRetryHandler.shouldRetry()){
    //do some repetitive work
     }

    //some code    
    }

или вы можете использовать его в рекурсивном методы:

void recursiveMethod(){
    //some code

    if(ShouldRetryHandler.shouldRetry()){
    recursiveMethod();
     }

    //some code    
    }
0
ответ дан Choletski 19 August 2018 в 00:39
поделиться
int retries = 3;
while (true)
{
    try
    {
        //Do Somthing
        break;
    }
    catch (Exception ex)
    {
        if (--retries == 0)
            return Request.BadRequest(ApiUtil.GenerateRequestResponse(false, "3 Times tried it failed do to : " + ex.Message, new JObject()));
        else
            System.Threading.Thread.Sleep(100);
    }
0
ответ дан Danh 19 August 2018 в 00:39
поделиться

Для тех, кто хочет иметь возможность повторить попытку в любом исключении или явно установить тип исключения, используйте это:

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);
    }
}
2
ответ дан Diran Ogunlana 19 August 2018 в 00:39
поделиться
public void TryThreeTimes(Action action)
{
    var tries = 3;
    while (true) {
        try {
            action();
            break; // success!
        } catch {
            if (--tries == 0)
                throw;
            Thread.Sleep(1000);
        }
    }
}

Затем вы вызывали бы:

TryThreeTimes(DoSomething);

... или альтернативно ...

TryThreeTimes(() => DoSomethingElse(withLocalVariable));

Более гибкая опция:

public void DoWithRetry(Action action, TimeSpan sleepPeriod, int tryCount = 3)
{
    if (tryCount <= 0)
        throw new ArgumentOutOfRangeException(nameof(tryCount));

    while (true) {
        try {
            action();
            break; // success!
        } catch {
            if (--tryCount == 0)
                throw;
            Thread.Sleep(sleepPeriod);
        }
   }
}

Используется как:

DoWithRetry(DoSomething, TimeSpan.FromSeconds(2), tryCount: 10);

Более современная версия с поддержкой async / await:

public async Task DoWithRetryAsync(Func<Task> action, TimeSpan sleepPeriod, int tryCount = 3)
{
    if (tryCount <= 0)
        throw new ArgumentOutOfRangeException(nameof(tryCount));

    while (true) {
        try {
            await action();
            return; // success!
        } catch {
            if (--tryCount == 0)
                throw;
            await Task.Delay(sleepPeriod);
        }
   }
}

Используется как :

await DoWithRetryAsync(DoSomethingAsync, TimeSpan.FromSeconds(2), tryCount: 10);
35
ответ дан Drew Noakes 19 August 2018 в 00:39
поделиться
  • 1
    желательно изменить if на: --retryCount <= 0, потому что это будет продолжаться вечно, если вы хотите отключить повторы, установив его на 0. Технически термин retryCount не является действительно хорошим именем, потому что он не будет повторять попытку, если вы установите это к 1. либо переименовать его в tryCount, либо поставить позади. – Stefanvds 31 May 2016 в 06:47
  • 2
    Такая трата нитей! – saille 3 October 2017 в 22:16
  • 3
    @saille Я согласен. Однако OP (и все остальные ответы) используют Thread.Sleep. Альтернативы - использовать таймеры или, скорее всего, в настоящее время использовать async для повторного запуска с помощью Task.Delay. – Drew Noakes 4 October 2017 в 10:22
  • 4
    Я добавил асинхронную версию. – Drew Noakes 4 October 2017 в 10:26
  • 5
    Только break , если действие returns true? Func<bool> – Kiquenet 18 April 2018 в 14:08

Возможно, это плохая идея. Во-первых, это символ символики «определение безумия делает одно и то же дважды и каждый раз ожидает разные результаты». Во-вторых, эта схема кодирования не скомбинирована с самим собой. Например:

Предположим, что ваш сетевой аппаратный уровень трижды повторяет пакет при сбое, ожидая, скажем, секунды между отказами.

Теперь предположим, что программный уровень повторно отправляет уведомление о сбое три раза при сбое пакетов.

Теперь предположим, что уровень уведомления повторно активирует уведомление три раза при сбое доставки уведомлений.

Теперь предположим, что слой сообщения об ошибках трижды активирует слой уведомления при сбое уведомления.

И теперь предположим, что веб-сервер повторно реагирует на сообщения об ошибках три раза при сбое ошибки.

Теперь предположим, что веб-клиент повторно отправляет запрос три раза при получении ошибки с сервера.

Теперь предположим, что линия сетевого коммутатора, которая должна перенаправить уведомление администратору, отключена. Когда пользователь веб-клиента наконец получит сообщение об ошибке? Я делаю это примерно через двенадцать минут.

Чтобы вы не подумали, что это всего лишь глупый пример: мы видели эту ошибку в коде клиента, хотя и намного хуже, чем я описал здесь. В конкретном коде клиента разрыв между состоянием ошибки и, наконец, сообщением пользователю, был несколько недель , потому что так много уровней автоматически повторялось с ожиданием. Представьте себе, что произойдет, если вместо три будут попытки повторить десять .

Обычно правильная вещь, связанная с условием ошибки, - report это немедленно и позволить пользователю решить, что делать. Если пользователь хочет создать политику автоматических попыток, пусть они создают эту политику на соответствующем уровне в абстракции программного обеспечения.

58
ответ дан Eric Lippert 19 August 2018 в 00:39
поделиться
  • 1
    +1. Раймонд поделился примером реальной жизни здесь, blogs.msdn.com/oldnewthing/archive/2005/11/07/489807.aspx – SolutionYogi 14 October 2009 в 14:57
  • 2
    -1 Этот совет бесполезен для кратковременных сбоев сети, с которыми сталкиваются автоматизированные системы пакетной обработки. – nohat 24 September 2010 в 22:22
  • 3
    Не уверен, что это говорит «Не делай этого». после чего следует «сделать это». Большинство людей, задающих этот вопрос, вероятно, являются людьми, работающими в программной абстракции. – Jim L 12 April 2012 в 17:30
  • 4
    Если у вас есть длительные пакетные задания, которые используют сетевые ресурсы, например веб-службы, вы не можете ожидать, что сеть будет на 100% надежной. Будут случайные таймауты, разъединения разъемов, возможно даже ложные сбои маршрутизации или сбои в работе сервера, которые возникают во время его использования. Один из вариантов - сбой, но это может означать перезапуск продолжительной работы позже. Другой вариант - повторить несколько раз с подходящей задержкой, чтобы определить, является ли это временной проблемой, а затем сбой. Я соглашаюсь с композицией, о которой вы должны знать ... но иногда это лучший выбор. – Erik Funkenbusch 5 February 2013 в 00:13
  • 5
    Я думаю, что цитата, которую вы использовали в начале вашего ответа, интересна. «Ожидание различных результатов» это только безумие, если предыдущий опыт дает вам одинаковые результаты. Хотя программное обеспечение построено на обещании согласованности, существуют определенные обстоятельства, при которых нам необходимо взаимодействовать с ненадежными силами вне нашего контроля. – Michael Richardson 21 May 2014 в 14:50

Использовать Polly

https://github.com/App-vNext/Polly-Samples

Вот повторитель, который я использую с Polly

public T Retry<T>(Func<T> action, int retryCount = 0)
{
    PolicyResult<T> policyResult = Policy
     .Handle<Exception>()
     .Retry(retryCount)
     .ExecuteAndCapture<T>(action);

    if (policyResult.Outcome == OutcomeType.Failure)
    {
        throw policyResult.FinalException;
    }

    return policyResult.Result;
}

Используйте его так

var result = Retry(() => MyFunction()), 3);
5
ответ дан Erik Bergstedt 19 August 2018 в 00:39
поделиться

Реализован ответ LBushkin последним способом:

    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);
    }  

и использовать его:

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

, тогда как функция [TaskFunction] может быть либо Task<T>, либо просто Task.

2
ответ дан Fabian Bigler 19 August 2018 в 00:39
поделиться

Основываясь на предыдущей работе, я подумал о том, чтобы усилить логику повтора тремя способами:

  1. Указание типа исключения для catch / retry. Это первичное улучшение, поскольку повторная попытка для любого исключения просто неверна.
  2. Не вложив последнюю попытку в try / catch, получив чуть более высокую производительность
  3. . Это расширение Action метода расширения
    static class ActionExtensions
    {
      public static void InvokeAndRetryOnException<T> (this Action action, int retries, TimeSpan retryDelay) where T : Exception
      {
        if (action == null)
          throw new ArgumentNullException("action");
    
        while( retries-- > 0 )
        {
          try
          {
            action( );
            return;
          }
          catch (T)
          {
            Thread.Sleep( retryDelay );
          }
        }
    
        action( );
      }
    }
    

Затем метод можно вызвать таким образом (конечно, можно использовать и анонимные методы):

new Action( AMethodThatMightThrowIntermittentException )
  .InvokeAndRetryOnException<IntermittentException>( 2, TimeSpan.FromSeconds( 1 ) );
7
ответ дан Igor Pashchuk 19 August 2018 в 00:39
поделиться
  • 1
    Это отлично. Но лично я бы не назвал его «retryTimeout», поскольку это не действительно тайм-аут. Возможно, «RetryDelay»? – Holf 10 June 2015 в 11:17
  • 2
    Согласен. Я обновил код по вашему предложению. – Igor Pashchuk 11 June 2015 в 17:56

Моя async реализация метода повтора:

public static async Task<T> DoAsync<T>(Func<dynamic> action, TimeSpan retryInterval, int retryCount = 3)
    {
        var exceptions = new List<Exception>();

        for (int retry = 0; retry < retryCount; retry++)
        {
            try
            {
                return await action().ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }

            await Task.Delay(retryInterval).ConfigureAwait(false);
        }
        throw new AggregateException(exceptions);
    }

Ключевые моменты: вместо этого я использовал .ConfigureAwait(false); и Func<dynamic> Func<T>

1
ответ дан Jeroen Vannevel 19 August 2018 в 00:39
поделиться
  • 1
    Это не дает ответа на вопрос. Пожалуйста, подумайте о том, чтобы опубликовать свой ответ в качестве нового вопроса, используя «Ask Question & quot; в верхней части страницы, а затем разместите свой собственный ответ на вопрос, чтобы поделиться тем, что вы узнали в сообществе. – Ed Cottrell♦ 7 March 2014 в 07:47
  • 2
    Гораздо проще с C # 5.0, чем codereview.stackexchange.com/q/55983/54000, но, возможно, CansellactionToken следует ввести. – SerG 11 December 2014 в 11:32

Держите его простым с 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);
    }
}
3
ответ дан Jogge 19 August 2018 в 00:39
поделиться
  • 1
    Мне любопытно, будет ли это порождать безумное количество потоков с высоким количеством повторных попыток и интервалом из-за возвращения того же ожидаемого метода? – HuntK24 15 June 2017 в 22:37

Я бы добавил следующий код к принятому ответу

public static class Retry<TException> where TException : Exception //ability to pass the exception type
    {
        //same code as the accepted answer ....

        public static T Do<T>(Func<T> action, TimeSpan retryInterval, int retryCount = 3)
        {
            var exceptions = new List<Exception>();

            for (int retry = 0; retry < retryCount; retry++)
            {
                try
                {
                    return action();
                }
                catch (TException ex) //Usage of the exception type
                {
                    exceptions.Add(ex);
                    Thread.Sleep(retryInterval);
                }
            }

            throw new AggregateException(String.Format("Failed to excecute after {0} attempt(s)", retryCount), exceptions);
        }
    }

. В основном приведенный выше код генерирует общий класс Retry, поэтому вы можете передать тип исключения, которое вы хотите поймать для повторной попытки .

Теперь используйте его почти таким же образом, но указав тип исключения

Retry<EndpointNotFoundException>.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));
0
ответ дан Juan M. Elosegui 19 August 2018 в 00:39
поделиться
  • 1
    Цикл for всегда будет выполняться пару раз (на основе вашего retryCount), даже если код в цикле TRY CATCH был выполнен без исключений. Я бы предложил установить retryCount равным retry var в цикле try, поэтому цикл for wil перестанет перебирать его. – scre_www 8 September 2017 в 08:32

Вы должны попробовать Полли . Это библиотека .NET, написанная мной, которая позволяет разработчикам свободно передавать временные политики обработки исключений, такие как Retry, Retry Forever, Wait and Retry или Circuit Breaker.

Пример

Policy
    .Handle<SqlException>(ex => ex.Number == 1205)
    .Or<ArgumentException>(ex => ex.ParamName == "example")
    .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(3))
    .Execute(() => DoSomething());
166
ответ дан Marcos Dimitrio 19 August 2018 в 00:39
поделиться
  • 1
    У вас есть пример этого в коде real ? – Doug 25 November 2015 в 01:00
  • 2
    Образцы Полли можно найти здесь: github.com/App-vNext/Polly-Samples – Flynn 29 December 2015 в 12:51
  • 3
    Плюс один для создания этого замечательного обязательного пакета. – AgentFire 21 December 2016 в 23:02
  • 4
    Что на самом деле является делегатом OnRetry? Я предполагаю, что это то, что нам нужно выполнить, когда произошло исключение. Поэтому, когда происходит исключение, делегат OnRetry будет вызывать и впоследствии выполнять делегирование. Это так? – user6395764 9 May 2018 в 09:44
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);          
      }
}
-1
ответ дан Mark P Neyer 19 August 2018 в 00:39
поделиться
  • 1
    Поскольку throw; является единственным способом завершения бесконечного цикла, этот метод фактически реализует «try», пока не завершится N раз ». а не желаемое «до времени N до достижения успеха». Вам понадобится break; или return; после вызова ThingToTryDelegate();, иначе он будет вызываться непрерывно, если он никогда не сработает. Кроме того, это не будет компилироваться, потому что первый параметр TryNTimes не имеет имени. -1. – BACON 7 September 2017 в 07:16

Я поклонник методов рекурсии и расширения, так что вот мои два цента:

public static void InvokeWithRetries(this Action @this, ushort numberOfRetries)
{
    try
    {
        @this();
    }
    catch
    {
        if (numberOfRetries == 0)
            throw;

        InvokeWithRetries(@this, --numberOfRetries);
    }
}
9
ответ дан Martin R-L 19 August 2018 в 00:39
поделиться

Блок приложений Transient Fault Handling предоставляет расширяемый набор стратегий повторения, включая:

  • Инкрементный
  • Фиксированный интервал
  • Экспоненциальное отключение

Оно также включает коллекцию стратегий обнаружения ошибок для облачных сервисов.

Подробнее см. в этой главе Руководства разработчика.

Доступно через NuGet (поиск « топаз »).

30
ответ дан Matthew Lock 19 August 2018 в 00:39
поделиться
  • 1
    Интересно. Можете ли вы использовать это за пределами Windows Azure, скажем, в приложении Winforms? – Matthew Lock 22 January 2013 в 07:18
  • 2
    Абсолютно. Используйте механизм повторного запуска ядра и предоставьте свои собственные стратегии обнаружения. Мы намеренно разделили их. Найдите основной пакет nuget здесь: nuget.org/packages/TransientFaultHandling.Core – Grigori Melnik 22 January 2013 в 17:13
  • 3
    Кроме того, проект теперь находится под Apache 2.0 и принимает взносы сообщества. aka.ms/entlibopen – Grigori Melnik 3 December 2013 в 02:50
  • 4
    @Alex. Кусочки его превращают в платформу. – Grigori Melnik 14 April 2015 в 16:15
  • 5
    Это теперь устарело, и, наконец, я использовал его, он содержал некоторые ошибки, которые, насколько я знаю, не были и никогда не будут исправлены: github.com/MicrosoftArchive/… . – Ohad Schneider 22 August 2017 в 14:12

Или как насчет того, чтобы делать это немного аккуратно ....

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

Я полагаю, что исключение исключений в металле можно исключить как механизм, если вы не проходите мимо них между границами (например, люди могут использовать). Почему бы просто не вернуть команду DoSomething() true, если она была успешной, и false в противном случае?

EDIT: И это может быть инкапсулировано внутри функции, как и другие. Проблема только в том, что вы сами не записываете функцию DoSomething()

0
ответ дан mike 19 August 2018 в 00:39
поделиться
  • 1
    «Я полагаю, что исключение исключений для исключения исключений следует рассматривать как механизм, если вы не проходите мимо них между границами». - Я полностью не согласен. Как вы узнали, что вызывающий абонент проверил ваш ложный (или худший, нулевой) возврат? ПОЧЕМУ произошел сбой кода? False ничего не говорит вам. Что делать, если вызывающий должен передать сбой в стек? Прочитайте msdn.microsoft.com/en-us/library/ms229014.aspx - они предназначены для библиотек, но они делают столько же смысла для внутреннего кода. И в команде другие люди, скорее всего, назовут ваш код. – TrueWill 14 October 2009 в 02:28

Я написал небольшой класс на основе ответов, размещенных здесь. Надеюсь, это поможет кому-то: https://github.com/natenho/resiliency

using System;
using System.Threading;

/// <summary>
/// Classe utilitária para suporte a resiliência
/// </summary>
public sealed class Resiliency
{
    /// <summary>
    /// Define o valor padrão de número de tentativas
    /// </summary>
    public static int DefaultRetryCount { get; set; }

    /// <summary>
    /// Define o valor padrão (em segundos) de tempo de espera entre tentativas
    /// </summary>
    public static int DefaultRetryTimeout { get; set; }

    /// <summary>
    /// Inicia a parte estática da resiliência, com os valores padrões
    /// </summary>
    static Resiliency()
    {
        DefaultRetryCount = 3;
        DefaultRetryTimeout = 0;
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente DefaultRetryCount vezes  quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Não aguarda para realizar novas tentativa.</remarks>
    public static void Try(Action action)
    {
        Try<Exception>(action, DefaultRetryCount, TimeSpan.FromMilliseconds(DefaultRetryTimeout), null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount">Número de novas tentativas a serem realizadas</param>
    /// <param name="retryTimeout">Tempo de espera antes de cada nova tentativa</param>
    public static void Try(Action action, int retryCount, TimeSpan retryTimeout)
    {
        Try<Exception>(action, retryCount, retryTimeout, null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount">Número de novas tentativas a serem realizadas</param>
    /// <param name="retryTimeout">Tempo de espera antes de cada nova tentativa</param>
    /// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
    public static void Try(Action action, int retryCount, TimeSpan retryTimeout, Action<ResiliencyTryHandler<Exception>> tryHandler)
    {
        Try<Exception>(action, retryCount, retryTimeout, tryHandler);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente por até DefaultRetryCount vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
    /// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Aguarda DefaultRetryTimeout segundos antes de realizar nova tentativa.</remarks>
    public static void Try(Action action, Action<ResiliencyTryHandler<Exception>> tryHandler)
    {
        Try<Exception>(action, DefaultRetryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="TException"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Aguarda DefaultRetryTimeout segundos antes de realizar nova tentativa.</remarks>
    public static void Try<TException>(Action action) where TException : Exception
    {
        Try<TException>(action, DefaultRetryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="TException"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount"></param>
    public static void Try<TException>(Action action, int retryCount) where TException : Exception
    {
        Try<TException>(action, retryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount"></param>
    /// <param name="retryTimeout"></param>
    public static void Try<TException>(Action action, int retryCount, TimeSpan retryTimeout) where TException : Exception
    {
        Try<TException>(action, retryCount, retryTimeout, null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
    /// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Aguarda DefaultRetryTimeout segundos antes de realizar nova tentativa.</remarks>
    public static void Try<TException>(Action action, Action<ResiliencyTryHandler<TException>> tryHandler) where TException : Exception
    {
        Try(action, DefaultRetryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), tryHandler);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada uma <see cref="Exception"/> definida no tipo genérico
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount">Número de novas tentativas a serem realizadas</param>
    /// <param name="retryTimeout">Tempo de espera antes de cada nova tentativa</param>
    /// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
    /// <remarks>Construído a partir de várias ideias no post <seealso cref="http://stackoverflow.com/questions/156DefaultRetryCount191/c-sharp-cleanest-way-to-write-retry-logic"/></remarks>
    public static void Try<TException>(Action action, int retryCount, TimeSpan retryTimeout, Action<ResiliencyTryHandler<TException>> tryHandler) where TException : Exception
    {
        if (action == null)
            throw new ArgumentNullException(nameof(action));

        while (retryCount-- > 0)
        {
            try
            {
                action();
                return;
            }
            catch (TException ex)
            {
                //Executa o manipulador de exception
                if (tryHandler != null)
                {
                    var callback = new ResiliencyTryHandler<TException>(ex, retryCount);
                    tryHandler(callback);
                    //A propriedade que aborta pode ser alterada pelo cliente
                    if (callback.AbortRetry)
                        throw;
                }

                //Aguarda o tempo especificado antes de tentar novamente
                Thread.Sleep(retryTimeout);
            }
        }

        //Na última tentativa, qualquer exception será lançada de volta ao chamador
        action();
    }

}

/// <summary>
/// Permite manipular o evento de cada tentativa da classe de <see cref="Resiliency"/>
/// </summary>
public class ResiliencyTryHandler<TException> where TException : Exception
{
    #region Properties

    /// <summary>
    /// Opção para abortar o ciclo de tentativas
    /// </summary>
    public bool AbortRetry { get; set; }

    /// <summary>
    /// <see cref="Exception"/> a ser tratada
    /// </summary>
    public TException Exception { get; private set; }

    /// <summary>
    /// Identifca o número da tentativa atual
    /// </summary>
    public int CurrentTry { get; private set; }

    #endregion

    #region Constructors

    /// <summary>
    /// Instancia um manipulador de tentativa. É utilizado internamente
    /// por <see cref="Resiliency"/> para permitir que o cliente altere o
    /// comportamento do ciclo de tentativas
    /// </summary>
    public ResiliencyTryHandler(TException exception, int currentTry)
    {
        Exception = exception;
        CurrentTry = currentTry;
    }

    #endregion

}
-1
ответ дан natenho 19 August 2018 в 00:39
поделиться

Мне нужно было передать некоторый параметр моему методу для повторения и получить значение результата; так что мне нужно выражение .. Я создаю этот класс, который выполняет эту работу (она вдохновлена ​​на LBushkin's), вы можете использовать ее следующим образом:

static void Main(string[] args)
{
    // one shot
    var res = Retry<string>.Do(() => retryThis("try"), 4, TimeSpan.FromSeconds(2), fix);

    // delayed execute
    var retry = new Retry<string>(() => retryThis("try"), 4, TimeSpan.FromSeconds(2), fix);
    var res2 = retry.Execute();
}

static void fix()
{
    Console.WriteLine("oh, no! Fix and retry!!!");
}

static string retryThis(string tryThis)
{
    Console.WriteLine("Let's try!!!");
    throw new Exception(tryThis);
}

public class Retry<TResult>
{
    Expression<Func<TResult>> _Method;
    int _NumRetries;
    TimeSpan _RetryTimeout;
    Action _OnFailureAction;

    public Retry(Expression<Func<TResult>> method, int numRetries, TimeSpan retryTimeout, Action onFailureAction)
    {
        _Method = method;
        _NumRetries = numRetries;
        _OnFailureAction = onFailureAction;
        _RetryTimeout = retryTimeout;
    }

    public TResult Execute()
    {
        TResult result = default(TResult);
        while (_NumRetries > 0)
        {
            try
            {
                result = _Method.Compile()();
                break;
            }
            catch
            {
                _OnFailureAction();
                _NumRetries--;
                if (_NumRetries <= 0) throw; // improved to avoid silent failure
                Thread.Sleep(_RetryTimeout);
            }
        }
        return result;
    }

    public static TResult Do(Expression<Func<TResult>> method, int numRetries, TimeSpan retryTimeout, Action onFailureAction)
    {
        var retry = new Retry<TResult>(method, numRetries, retryTimeout, onFailureAction);
        return retry.Execute();
    }
}

ps. решение Л.Бушкина делает еще один повтор = D

0
ответ дан Paolo Sanchi 19 August 2018 в 00:39
поделиться

Мне нужен был метод, который поддерживает отмену, в то время как я был на нем, я добавил поддержку для возврата промежуточных отказов.

public static class ThreadUtils
{
    public static RetryResult Retry(
        Action target,
        CancellationToken cancellationToken,
        int timeout = 5000,
        int retries = 0)
    {
        CheckRetryParameters(timeout, retries)
        var failures = new List<Exception>();
        while(!cancellationToken.IsCancellationRequested)
        {
            try
            {
                target();
                return new RetryResult(failures);
            }
            catch (Exception ex)
            {
                failures.Add(ex);
            }

            if (retries > 0)
            {
                retries--;
                if (retries == 0)
                {
                    throw new AggregateException(
                     "Retry limit reached, see InnerExceptions for details.",
                     failures);
                }
            }

            if (cancellationToken.WaitHandle.WaitOne(timeout))
            {
                break;
            }
        }

        failures.Add(new OperationCancelledException(
            "The Retry Operation was cancelled."));
        throw new AggregateException("Retry was cancelled.", failures);
    }

    private static void CheckRetryParameters(int timeout, int retries)
    {
        if (timeout < 1)
        {
            throw new ArgumentOutOfRangeException(...
        }

        if (retries < 0)
        {
            throw new ArgumentOutOfRangeException(...

        }
    }

    public class RetryResult : IEnumerable<Exception>
    {
        private readonly IEnumerable<Exception> failureExceptions;
        private readonly int failureCount;

         protected internal RetryResult(
             ICollection<Exception> failureExceptions)
         {
             this.failureExceptions = failureExceptions;
             this.failureCount = failureExceptions.Count;
         }
    }

    public int FailureCount
    {
        get { return this.failureCount; }
    }

    public IEnumerator<Exception> GetEnumerator()
    {
        return this.failureExceptions.GetEnumerator();
    }

    System.Collections.IEnumerator 
        System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

Вы можете использовать функцию Retry, как это, повторите попытку 3 раза с задержка на 10 секунд, но без отмены.

try
{
    var result = ThreadUtils.Retry(
        SomeAction, 
        CancellationToken.None,
        10000,
        3);

    // it worked
    result.FailureCount // but failed this many times first.
}
catch (AggregationException ex)
{
   // oops, 3 retries wasn't enough.
}

Или повторите попытку каждые пять секунд, если не будет отменена.

try
{
    var result = ThreadUtils.Retry(
        SomeAction, 
        someTokenSource.Token);

    // it worked
    result.FailureCount // but failed this many times first.
}
catch (AggregationException ex)
{
   // operation was cancelled before success.
}

Как вы можете догадаться, ve перегрузил функцию Retry для поддержки различных типов дегатов, которые я хочу использовать.

1
ответ дан Patrick 19 August 2018 в 00:39
поделиться

Я бы выполнил это:

public static bool Retry(int maxRetries, Func<bool, bool> method)
{
    while (maxRetries > 0)
    {
        if (method(maxRetries == 1))
        {
            return true;
        }
        maxRetries--;
    }
    return false;        
}

Я бы не использовал исключения, как они используются в других примерах. Мне кажется, что если мы ожидаем, что метод не удастся, его отказ не является исключением. Таким образом, метод, который я вызываю, должен возвращать true, если он преуспел, и false, если он потерпел неудачу.

Почему это Func<bool, bool>, а не только Func<bool>? Поэтому, если я хочу , чтобы метод мог генерировать исключение при ошибке, у меня есть способ сообщить ему, что это последняя попытка.

Поэтому я могу использовать его с кодом типа:

Retry(5, delegate(bool lastIteration)
   {
       // do stuff
       if (!succeeded && lastIteration)
       {
          throw new InvalidOperationException(...)
       }
       return succeeded;
   });

или

if (!Retry(5, delegate(bool lastIteration)
   {
       // do stuff
       return succeeded;
   }))
{
   Console.WriteLine("Well, that didn't work.");
}

Если передача параметра, который метод не использует, доказывает быть неудобным, тривиально реализовать перегрузку Retry, которая просто принимает значение Func<bool>.

4
ответ дан Robert Rossney 19 August 2018 в 00:39
поделиться
  • 1
    +1 для исключения исключения. Хотя я бы сделал пустоту Retry (...) и бросил что-то? Булевы возвращения и / или коды возврата слишком часто игнорируются. – csharptest.net 14 October 2009 в 00:22
  • 2
    «если мы ожидаем, что метод не удастся, его сбой не является исключением», - в то время как это верно в некоторых случаях, исключение не обязательно должно быть исключительным. Это для обработки ошибок. Нет гарантии, что вызывающий абонент проверит логический результат. Там является гарантией того, что исключение будет обработано (при запуске приложения, если ничего не делает). – TrueWill 14 October 2009 в 02:17
  • 3
    Я не могу найти ссылку, но я считаю, что .NET определяет исключение как «метод не сделал то, что он сказал, что это будет делать». 1 предназначена для использования исключений, указывающих на проблему, а не на шаблон Win32, требующий от вызывающего пользователя проверить возвращаемое значение, если функция выполнена успешно или нет. – noctonura 14 October 2009 в 05:03
  • 4
    Но исключения не просто «указывают на проблему». Они также включают массу диагностической информации, которая требует времени и памяти для компиляции. Есть четкие ситуации, когда это не имеет никакого значения. Но есть много, где это происходит. .NET не использует исключения для потока управления (сравните, скажем, с использованием Python исключения StopIteration), и есть причина. – Robert Rossney 14 October 2009 в 09:56

Экспоненциальная отсрочка - хорошая стратегия повторения, чем просто попытка x раз. Вы можете использовать библиотеку, такую ​​как Polly, для ее реализации.

1
ответ дан utsavized 19 August 2018 в 00:39
поделиться

Вы также можете подумать о добавлении типа исключения, для которого вы хотите повторить попытку. Например, это исключение таймаута, которое вы хотите повторить? Исключение базы данных?

RetryForExcpetionType(DoSomething, typeof(TimeoutException), 5, 1000);

public static void RetryForExcpetionType(Action action, Type retryOnExceptionType, int numRetries, int retryTimeout)
{
    if (action == null)
        throw new ArgumentNullException("action");
    if (retryOnExceptionType == null)
        throw new ArgumentNullException("retryOnExceptionType");
    while (true)
    {
        try
        {
            action();
            return;
        }
        catch(Exception e)
        {
            if (--numRetries <= 0 || !retryOnExceptionType.IsAssignableFrom(e.GetType()))
                throw;

            if (retryTimeout > 0)
                System.Threading.Thread.Sleep(retryTimeout);
        }
    }
}

Вы также можете заметить, что все остальные примеры имеют аналогичную проблему с тестированием для повторений == 0 и либо повторять бесконечность, либо не могут генерировать исключения при предоставлении отрицательного значения. Кроме того, спящий режим (-1000) выйдет из строя выше. Зависит от того, как «глупый» вы ожидаете, что люди будут только защитным программированием, никогда не болит.

14
ответ дан Zain Rizvi 19 August 2018 в 00:39
поделиться
  • 1
    +1, но почему бы не сделать RetryForException & lt; T & gt; (...) где T: Исключение, затем catch (T e)? Просто попробовал, и он отлично работает. – TrueWill 14 October 2009 в 02:23
  • 2
    Либо или здесь, так как мне не нужно ничего делать с типом, если бы я понял, что простой старый параметр мог бы сделать трюк. – csharptest.net 14 October 2009 в 02:49
  • 3
    @True По-видимому, catch (T ex) имеет некоторые ошибки в соответствии с этим сообщением stackoverflow.com/questions/1577760/… – csharptest.net 16 October 2009 в 15:15
  • 4
    Обновление: на самом деле лучшая реализация, которую я использовал, использует Predicate & lt; Exception & gt; делегат, который возвращает true, если повторение является подходящим. Это позволяет использовать собственные коды ошибок или другие свойства исключения, чтобы определить, применима ли повторная попытка. Например, коды HTTP 503. – csharptest.net 16 April 2012 в 19:44
  • 5
    & quot; Также Sleep (-1000) завершит неудачу в блоках захвата выше & quot; ... используйте TimeSpan, и вы не получите эту проблему. Плюс TimeSpan гораздо более гибкий и самоописательный. Из вашей подписи & quot; int retryTimeout & quot; как узнать, если retryTimeout - это MS, секунды, минуты, годы ?? ;-) – bytedev 24 August 2016 в 10:23
Другие вопросы по тегам:

Похожие вопросы: