Это безопасно.
Это метод 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
внутренне, поэтомуэто хорошее приближение).