在无限循环中等待Task.Delay

时间:2019-02-19 11:28:17

标签: c# memory-leaks

我有以下代码,它将执行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 :(得分:1)

这很安全。

这是一个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;
}

当然,实际的编译代码看起来并不像这样(it looks like this),但这说明了我的观点。

这里没有递归。甚至没有无限循环。您有一个方法,该方法在被调用时会尝试执行您的操作。如果失败,则将其排队到ThreadPool上并返回。对ThreadPool.QueueUserWorkItem的调用会立即返回,并且不会等到工作完成。

ThreadPool.QueueUserWorkItem也不创建新线程-它将要由一个预先存在的线程池执行的工作排队。

(在有人发表评论之前-我知道实际的编译后的代码不会直接使用ThreadPool.QueueUserWorkItem,但可能会使用默认的TaskScheduler,它在内部调用ThreadPool.UnsafeQueueUserWorkItem,因此是一个很好的近似值)

答案 1 :(得分:0)

我认为它不会,因为一旦满足retryCount条件,它要么通过break要么通过throw退出循环。取决于您的retryCount会导致内存泄漏的发生方式。