В ожидании выполнения Task.Delay в бесконечном цикле - PullRequest
0 голосов
/ 19 февраля 2019

У меня есть код ниже, который будет выполнять Action operation, если в нем произойдет исключение, он будет повторен.

Поскольку код await Task.Delay(delay); находится в пределах while(true), ясомневаетесь, не вызовет ли это утечки памяти?Как создание бесконечного threads?

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

public class OperationWithBasicRetry
{
  public async Task StartOperationAsync(Action operation, TimeSpan delay, int retryCount)
  {
    int currentRetry = 0;

    while(true)
    {
      try
      {
        operation();

        // Success
        break;
      }
      catch (Exception ex)
      {
        if (++currentRetry > retryCount)
        {
          throw;
        }
      }

      await Task.Delay(delay);
    }
  }
}

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

Это безопасно.

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

private class State
{
    public int currentRetry;
    public Action operation;
    public TimeSpan delay;
    public int retryCount;
    public TaskCompletionSource<object> tcs;

    private void StartOperationAsyncImpl(object unused)
    {
        try
        {
            operation();
            tcs.SetResult(null);
            return;
        }
        catch (Exception ex)
        {
            if (++currentRetry > retryCount)
            {
                tcs.SetException(ex);
            }
        }

        // I'm ignoring the delay bit, because it has no affect on the point
        // I'm trying to make.
        ThreadPool.QueueUserWorkItem(StateOperationAsyncImpl);
    }
}

public Task StartOperationAsync(Action operation, TimeSpan delay, int retryCount)
{
    State state = new State();
    state.currentRetry = 0;
    state.operation = operation;
    state.delay = delay;
    state.retryCount = retryCount;
    state.tcs = new TaskCompletionSource<object>();

    ThreadPool.QueueUserWorkItem(state.StartOperationAsyncImpl);

    return state.tcs;
}

Конечно, фактический скомпилированный код выглядит совсем не так (это выглядит так ), но это иллюстрирует мою точку зрения.

Здесь рекурсии не происходит.Там нет даже бесконечного цикла.У вас есть метод, который при вызове пытается выполнить вашу операцию.Если это не удается, то он ставит себя в очередь на ThreadPool и возвращаетЭтот вызов ThreadPool.QueueUserWorkItem сразу возвращается, и он не ждет, пока работа будет завершена.

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

(Прежде, чем кто-то прокомментирует - я знаю, что фактический скомпилированный код не будет использовать ThreadPool.QueueUserWorkItem напрямую, но он, вероятно, будет использовать TaskScheduler по умолчанию, который вызывает ThreadPool.UnsafeQueueUserWorkItem внутренне, поэтомуэто хорошее приближение).

0 голосов
/ 19 февраля 2019

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...