usr*_*usr 16 c# task-parallel-library async-await c#-5.0
等待故障任务(具有异常集的任务)时,await
将重新抛出存储的异常.如果存储的异常是a AggregateException
,它将重新抛出第一个并丢弃其余的异常.
我们如何使用await
并同时抛出原件AggregateException
以便我们不会意外丢失错误信息?
注意,当然可以考虑使用hacky解决方案(例如,试一试await
,然后调用Task.Wait
).我真的希望找到一个干净的解决方案.这里最好的做法是什么?
我想过使用自定义awaiter,但内置TaskAwaiter
包含很多魔法,我不知道如何完全重现.它调用TPL类型的内部API.我也不想重现所有这些.
如果你想玩它,这是一个简短的repro:
static void Main()
{
Run().Wait();
}
static async Task Run()
{
Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
await Task.WhenAll(tasks);
}
static Task CreateTask(string message)
{
return Task.Factory.StartNew(() => { throw new Exception(message); });
}
Run Code Online (Sandbox Code Playgroud)
只抛出两个例外中的一个Run
.
请注意,Stack Overflow上的其他问题无法解决此特定问题.建议重复时请小心.
Ste*_*ary 23
我不同意你的问题标题中暗示await
行为是不受欢迎的.在绝大多数场景中都有意义.在一个WhenAll
情况下,你多久真的需要知道所有的错误的详细信息,而不是只有一个?
主要的困难AggregateException
是异常处理,即你失去了捕获特定类型的能力.
也就是说,使用扩展方法很容易获得所需的行为:
public static async Task WithAggregateException(this Task source)
{
try
{
await source.ConfigureAwait(false);
}
catch
{
// source.Exception may be null if the task was canceled.
if (source.Exception == null)
throw;
// EDI preserves the original exception's stack trace, if any.
ExceptionDispatchInfo.Capture(source.Exception).Throw();
}
}
Run Code Online (Sandbox Code Playgroud)
这是 Stephen ClearyWithAggregateException
扩展方法的一个较短的实现:
public static async Task WithAggregateException(this Task source)
{
try { await source.ConfigureAwait(false); }
catch when (source.IsCanceled) { throw; }
catch { source.Wait(); }
}
public static async Task<T> WithAggregateException<T>(this Task<T> source)
{
try { return await source.ConfigureAwait(false); }
catch when (source.IsCanceled) { throw; }
catch { return source.Result; }
}
Run Code Online (Sandbox Code Playgroud)
此方法基于 Stephen Toub 在GitHub 的 API提案中的建议。
更新:我添加了对取消情况的特殊处理,以防止传播AggregateException
包含OperationCanceledException
. 现在OperationCanceledException
直接传播,并且Task.IsCanceled
状态被保留。感谢@noseratio 在这个答案的评论中指出了这个缺陷。当然,现在这个实现并不比 Stephen Cleary 的方法短多少!
我知道我迟到了,但我发现这个巧妙的小技巧可以满足您的需求。由于完整的异常集可用于等待的任务,因此调用此任务的 Wait 或 .Result 将引发聚合异常。
static void Main(string[] args)
{
var task = Run();
task.Wait();
}
public static async Task Run()
{
Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
var compositeTask = Task.WhenAll(tasks);
try
{
await compositeTask.ContinueWith((antecedant) => { }, TaskContinuationOptions.ExecuteSynchronously);
compositeTask.Wait();
}
catch (AggregateException aex)
{
foreach (var ex in aex.InnerExceptions)
{
Console.WriteLine(ex.Message);
}
}
}
static Task CreateTask(string message)
{
return Task.Factory.StartNew(() => { throw new Exception(message); });
}
Run Code Online (Sandbox Code Playgroud)