我试图理解 .net8 中的 async/await,特别是异常处理,但我遇到了一些困惑,特别是关于文档,因为它指定了一些方法,但没有提供足够的示例让我理解什么是正在发生。
我使用以下简单代码只是为了观看某些内容的工作:
private static async Task Main(string[] args) {
var sleeps = new int[] { 1, 2, 3, 0 };
var tasks = new List<Task<int>>();
foreach (var t in sleeps)
{
Console.WriteLine("Submitting task: {0}", t);
tasks.Add(sleep(t));
}
Console.WriteLine("All tasks submitted, waiting for all to complete");
try
{
Task.WaitAll(tasks.ToArray());
} catch (DivideByZeroException e)
{
Console.WriteLine("One of the tasks failed: {0}", e);
}
Console.WriteLine("All tasks completed, collecting results");
foreach (var t in tasks)
{
Console.WriteLine("Task returned: {0}", t.Result);
}
}
private static async Task<int> sleep(int s)
{
Console.WriteLine("Sleeping for: [ {0} ]", s);
if (s == 0)
{
// this is just to throw something to be caught
throw new DivideByZeroException("0 can't be delayed");
}
await Task.Delay(s * 1000);
Console.WriteLine("\tSlept for: [ {0} ]", s);
return s;
}
我正在关注这里的文档:
这里:
根据文档,我应该在进入代码的异步部分之前尝试在异步函数中抛出异常。您可以在我的示例中看到,我在等待 Task.Delay 之前抛出 ZeroDivision 异常。
当我在 Visual Studio 中运行此代码时,我希望能够命中 DivideByZeroException catch 块,但我得到了一个 SystemAggregateException,我理解这是 .net 如何收集所有异步异常,但是文档明确指出:
When code awaits a faulted task, the first exception in the AggregateException.InnerExceptions collection is rethrown
。但是,我根本没有抛出 DivideByZero 异常,而是依次抛出 2 个 SystemAggregate 异常,每个异常的第一个 innerException 是 DivideByZero 异常。
这里发生了什么事。
我还在这里编写了早餐示例:使用 async 和 wait 进行异步编程,并添加了烤面包机着火,这更加令人困惑,因为当我显式抛出异常时,我不会在任何地方抛出异常。我必须检查
Task.Exception
看看它是否被抛出。
那么谁能解释一下这里发生了什么?
如果我想提交 5 个异步任务,等待所有任务完成,然后迭代它们以检查它们的
Result
或 Exception
属性,我会使用什么方法?我不明白何时尝试/捕获、何时检查 Task.Exception 以及为什么我在示例代码中遇到 SystemAggregate 异常。
异步
AggregateException
方法不会抛出 sleep
。它是由同步 Task.WaitAll
方法抛出的:
例外情况
聚合异常 至少有一个
实例被取消。如果任务被取消,Task
会在其AggregateException
集合中包含OperationCanceledException
。InnerExceptions
解决您问题的最佳解决方案很可能将
Task.WaitAll
替换为异步 Task.WhenAll
:
try
{
await Task.WhenAll(tasks.ToArray());
}
catch (DivideByZeroException e)
{
Console.WriteLine("One of the tasks failed: {0}", e);
}
这也将消除您可能看到的关于不包含任何 async Task Main
的
await
方法的 警告。
如果您想同步等待任务,并且您喜欢
await
只传播一个异常的行为,您可以像这样使用 .GetAwaiter().GetResult()
:
try
{
Task.WhenAll(tasks.ToArray()).GetAwaiter().GetResult();
}
catch (DivideByZeroException e)
{
Console.WriteLine("One of the tasks failed: {0}", e);
}
这将阻塞当前线程,直到所有任务完成,并将根据 .NET 运行时版本,按位置或按时间顺序传播失败任务之一的异常。您可以查看此 GitHub 问题,了解有关传播哪个任务的异常的详细信息。