如何在 C# 中设置一组异步任务并处理源错误



如果我有一组方法要调用,并且想要异步调用。我可以这样做:

var status = new DiagnosticsResult();
try
{
var taskList = new List<Task>
{
_someService.Method1(),
_someService.Method2(),
_someService.Method3(),
_someService.Method4(),
_someService.Method5(),
_someService.Method6(),
_someService.Method7(),
_someService.Method8()
};
Task.WaitAll(taskList.ToArray());
}
catch (AggregateException ex)
{
foreach (var innerException in ex.InnerExceptions)
{
// Do Something
}
}

如果我只想知道发生了错误,那很好,但是如果我需要知道哪些方法抛出异常以及哪些方法成功了怎么办?

如果您将任务列表保留在范围内,则可以在catch中迭代它们,或者只是在Wait之后迭代它们以查找Task.Exception的异常。

您可以将AggregateException.HandleFunc<Exception, bool>一起使用,该可让您过滤消息、类型等。 此外,如果遍历Aggregate,则应调用Flatten来排列所有异常。

异常处理 MSDN

编辑

我面临的问题是,一旦抛出其余部分,就会更改为"已取消"状态。我需要它们全部运行并通过或失败,然后我需要确定结果。我将任务列表移到了尝试之外,是的,它已更新,但是当#4失败时,我得到3个"RanToCompletion",1个"错误",其余的"取消">

似乎这种方法是等待每个并单独处理它们的异常,而不是作为组处理。下面显示的两种方法:

public async Task Test1() {
var taskList = new List<Task>() {
_someService.Method1.ContinueWith(tsk => {
//handle ex
}).Unwrap()
}
/*
* or..
* */
try {
await _someService.Method1
}catch (AggregateException ex) {
/***
* Handle ex
* *//
}               
}

一种可能的方法是为每个任务传递一个令牌,并在评估结果时启用关联:

public static Task<WhenAllWithTokenResult<TToken>> WhenAllWithToken<TToken>(Tuple<Task, TToken>[] tasks)
{
var successfulTasks = new List<Tuple<Task, TToken>>();
var failedTasks = new List<Tuple<Task, TToken>>();
var cancelledTasks = new List<Tuple<Task, TToken>>();
int amountCompleted = 0;
var taskCompletionSource = new TaskCompletionSource<WhenAllWithTokenResult<TToken>>();
// Register ContinueWith callback for each task and add it to the according result list when completed
foreach (var tuple in tasks)
{
var copyOfTuple = tuple;
tuple.Item1.ContinueWith(_ =>
{               
if (_.IsFaulted)
{
failedTasks.Add(copyOfTuple);
}
else if (_.IsCanceled)
{
cancelledTasks.Add(copyOfTuple);
}
else if (_.IsCompleted)
{
successfulTasks.Add(copyOfTuple);
}
if (Interlocked.Increment(ref amountCompleted) == tasks.Length)
{
// All tasks finished so let's set the result of this method
taskCompletionSource.SetResult(new WhenAllWithTokenResult<TToken>(successfulTasks, failedTasks, cancelledTasks));
}
});
}
return taskCompletionSource.Task;
}
public class WhenAllWithTokenResult<TToken>
{
public IList<Tuple<Task, TToken>> SuccessfulTasks { get; private set; }
public IList<Tuple<Task, TToken>> FailedTasks { get; private set; }
public IList<Tuple<Task, TToken>> CancelledTasks { get; private set; }
public WhenAllWithTokenResult(IList<Tuple<Task, TToken>> successfulTasks, IList<Tuple<Task, TToken>> failedTasks, IList<Tuple<Task, TToken>> cancelledTasks)
{
CancelledTasks = cancelledTasks;
SuccessfulTasks = successfulTasks;
FailedTasks = failedTasks;
}
}

并像这样使用它:

WhenAllWithTokenResult<string> result = 
await WhenAllWithToken(
new[]
{
Tuple.Create(_someService.Method1(), "Method1"),
Tuple.Create(_someService.Method2(), "Method2"),
Tuple.Create(_someService.Method3(), "Method3"),
Tuple.Create(_someService.Method4(), "Method4")
});
foreach (var item in result.SuccessfulTasks)
{
Console.WriteLine("Successful: {0}", item.Item2);
}
foreach (var item in result.FailedTasks)
{
Console.WriteLine("Failed: {0}", item.Item2);
}
foreach (var item in result.CancelledTasks)
{
Console.WriteLine("Cancelled: {0}", item.Item2);
}

请注意 1)令牌是通用的,只是传递以允许关联。因此,它可以是您想要的任何东西。2)WhenAllWithToken与WhenAll具有不同的语义:它返回一个任务,当所有输入任务成功,失败或取消时完成,但之前没有

最新更新