Я создаю приложение, которое должно выполнять много параллельных задач.Эти задачи содержат несколько вызовов различных асинхронных методов (в основном это запросы к некоторым API REST с использованием HttpClient) с некоторой промежуточной обработкой, и я действительно не хочу перехватывать исключения внутри самой задачи.Вместо этого я бы предпочел сделать это, ожидая их, используя методы WhenAny / WhenAll.При получении исключения мне также необходимо собрать некоторые исходные данные этой задачи для последующего анализа (скажем, записать их в журнал).
Есть свойство Task.AsyncState, которое кажется идеальным контейнером для этих данных.Итак, я передаю объект TaskState при создании задачи следующим образом:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ConsoleApp5
{
class Program
{
static void Main(string[] args)
{
new Program().RunTasksAsync();
Console.ReadLine();
}
class TaskState
{
public int MyValue { get; }
public TaskState(int myValue)
{
MyValue = myValue;
}
}
private async Task<int> AsyncMethod1(int value)
{
await Task.Delay(500);
return value + 1;
}
private async Task<int> AsyncMethod2(int value)
{
await Task.Delay(500);
if (value % 3 == 0)
throw new Exception("Expected exception");
else
return value * 2;
}
private async Task DoJobAsync(object state)
{
var taskState = state as TaskState;
var i1 = await AsyncMethod1(taskState.MyValue);
var i2 = await AsyncMethod2(i1);
Console.WriteLine($"The result is {i2}");
}
private async void RunTasksAsync()
{
const int tasksCount = 10;
var tasks = new List<Task>(tasksCount);
var taskFactory = new TaskFactory();
for (var i = 0; i < tasksCount; i++)
{
var state = new TaskState(i);
var task = taskFactory.StartNew(async state => await DoJobAsync(state), state).Unwrap();
tasks.Add(task);
}
while (tasks.Count > 0) {
var task = await Task.WhenAny(tasks);
tasks.Remove(task);
var state = task.AsyncState as TaskState;
if (task.Status == TaskStatus.Faulted)
Console.WriteLine($"Caught an exception while executing task, task data: {state?.MyValue}");
}
Console.WriteLine("All tasks finished");
}
}
}
Проблема с кодом выше заключается в том, что Unwrap () создает прокси-сервер задачи, который не имеет значения AsyncState, он всегда равен нулю, поэтомукогда дело доходит до исключения, я не могу получить исходное состояние задачи.Если я удаляю модификаторы Unwrap () и async / await в делегате, метод WaitAny () завершается до фактического завершения задачи.
Если я использую Task.Run(() => DoJobAsync(state))
вместо TaskFactory.StartNew (), tasksзакончить в правильном порядке, но таким образом я не могу установить AsyncState при создании задач.
Конечно, я могу использовать Dictionary<Task, TaskState>
для хранения параметров задач в случае сбоя, но мне это кажется немного грязным, и, по сравнению с использованием свойства AsyncState, для поиска потребуется времядля совпадения в этой коллекции в случае большого количества одновременных задач.
Может кто-нибудь предложить более элегантное решение в этом случае?Есть ли другой способ получить состояние задачи при исключении?