// повторное выполнение задачи, если требуется замена в массиве задач
Это первое, что я хотел бы изменить. Гораздо лучше , а не , чтобы приложение обрабатывало свой собственный "перезапуск". Если операция завершилась неудачно, нет гарантии, что приложение сможет восстановиться. Это верно для любых операций на любом языке / во время выполнения.
Лучшее решение состоит в том, чтобы позволить другому приложению перезапустить это. Разрешить распространение исключения (регистрация его, если это возможно), и разрешить ему завершить приложение. Затем при необходимости перезапустите процесс «менеджер» (буквально отдельный исполняемый процесс). Именно так работают все современные системы высокой доступности, от диспетчера служб Win32 до ASP.NET, диспетчера контейнеров Kubernetes и среды выполнения функций Azure.
Обратите внимание, что если вы хотите пойти по этому пути, возможно, имеет смысл разделить задачи на разные процессы, чтобы они могли быть перезапущены независимо. Таким образом, перезапуск в одном не вызовет перезапуск в других.
Однако, если вы хотите сохранить все свои задачи в одном процессе, то у вас есть подходящее решение. Если у вас есть известное количество задач в начале процесса, и это число не изменится (если они не будут выполнены), вы можете немного упростить код, исключив перезапуск и используя Task.WhenAll
вместо Task.WhenAny
:
async Task RunAsync(Func<CancellationToken, Task> work, CancellationToken token)
{
while (true)
{
try { await work(token); }
catch
{
// log...
}
if (we-should-not-restart)
break;
}
}
List<Func<CancellationToken, Task>> workToDo = ...;
var tasks = workToDo.Select(work => RunAsync(work, token));
await Task.WhenAll(tasks);
// Only gets here if they all complete/fail and were not restarted.
разработчик может не выполнить RunAsync и выполнить блокирующий вызов, в этом случае все приложение будет зависать.
Лучший способ предотвратить это - заключить вызов в Task.Run
, вот так:
await work(token);
становится таким:
await Task.Run(() => work(token));