c# - 当 try-catch 中有多个任务时,'finally' 块会运行一次吗?
问题描述
我有以下定期运行的 C# 代码,例如每 1 小时运行一次。每次运行时,它都会尝试并行进行一些远程调用。当这些远程调用中的任何一个引发异常时,我只创建一个票证,或更新之前的票证。并且在 finally 块中,如果这次没有异常,则解决现有的先前票证。本质上,我想确保无论有多少远程调用失败,我只有一张票可以调查。并且当下次所有调用都成功时,票证会自动解决。
但是,当远程调用在 try 块中全部成功时,finally 块中的调用尝试解析票证,但遇到 HTTP 412 Precondition Failed,这意味着我在 try 块中获得的票证在 finally 块之前以某种方式更新。如果它在同一个线程中更新,我不会尝试解决它。
我从这篇文章中了解到,即使存在失败(错误或取消的任务) ,Task.WhenAll
也会等待所有任务完成。如果多个任务抛出异常,catch 块会运行一次还是多次?finally 块呢?
Ticket ticket = null;
try
{
// Query to get the existing ticket if there is any.
ticket = await QueryExistingTicketAsync();
// Make a lot of remote calls in parallel
var myTasks = new List<Task>();
myTasks.Add(RemoteCallAsync("call1"));
myTasks.Add(RemoteCallAsync("call2"));
myTasks.Add(RemoteCallAsync("call3"));
// ... add more Tasks for RemoteCallAsync()
await Task.WhenAll(myTasks);
}
catch (Exception ex)
{
if (ticket != null)
{
ticket.ReChecked = true;
ticket.LastCheckTime = DateTimeOffset.Now;
// If the previous ticket exists, meaning the last run failed as well,
// update the timestamp on that ticket.
ticket = await UpdateExistingTicketAsync(ticket);
}
else
{
// If the previous ticket does not exist yet,
// create one ticket for investigation, this ticket.ReChecked will be true
ticket = await CreateNewTicketAsync();
}
}
finally
{
// Resolve the previous ticket if it was not created/updated in catch block
if (ticket != null && !ticket.ReChecked)
{
ticket.Status = "Resolved";
await UpdateExistingTicketAsync(ticket);
}
}
解决方案
简短的回答:即使多个任务抛出异常,也只会抛出一个异常await Task.WaitAll
,因此该catch
块只会执行一次。块总是在finally
每个进入块时执行一次try
。长答案如下。
等待 aTask.WaitAll
时,如果任何包装Task
的 s 抛出异常,AggregateException
则从所有Task
s 中抛出包含异常。所以假设你已经await Task.WhenAll(tasks)
毫无例外地到达了这条线,这就是发生的事情:
Task.WhenAll
将等待所有Task
s 完成,无论它们是否出错。- 如果所有的
Task
s 都成功完成,则Task.WhenAll
成功完成,在到达块的末尾处继续执行,到达块的await
末尾,块执行。try
finally
- 如果一个或多个
Task
s 以异常结束,Task.WhenAll
则将它们全部包装成 anAggregateException
并以故障状态完成。执行在 处恢复,并抛出await
上述内容。AggregateException
执行catch
块,然后执行finally
块。
因此,假设一个线程进入try
块,在这两种情况下,当await
只有一个线程继续执行时,都会继续执行。两次输入catch
orfinally
块的唯一方法是执行该方法两次。
该await
语句在幕后做了很多魔术,但它永远不会导致您在多个线程上继续执行。一个线程await
,只有一个线程捡起来继续。
推荐阅读
- filebeat - 如何禁用 filebeat 的 close_inactive 设置?
- c# - 当我点击谷歌对话流的端点但相同的谷歌对话流端点在邮递员中工作时,在 C# 中出现错误
- java - 将完整文件编码为 base64(不仅是内容)
- java - 如何用新数据替换arraylist中的现有元素
- python - 使用布尔值 AND 计算字符串在行中的出现次数
- vba - 查找单元格为空白时删除图像
- docker - CloudFoundry 上 docker 容器的健康检查失败:没有这样的文件或目录
- angular - 无法使用 angular-cli 在 2 个本地应用程序之间执行 http post/get 请求
- php - sql 无法通过 mysqli_multi_query 执行
- c++ - Microsoft/CppRestSDK 卡萨布兰卡,Visual Studio 2017