如何在不抛出 TaskCanceledExceptions 的情况下等待任务?

Posted

技术标签:

【中文标题】如何在不抛出 TaskCanceledExceptions 的情况下等待任务?【英文标题】:How can I wait on tasks without throwing TaskCanceledExceptions? 【发布时间】:2012-01-30 16:21:21 【问题描述】:

我有一个方法可以创建一些任务,然后在返回之前使用WaitAll 等待它们。问题是,如果这些任务被取消,WaitAll 会抛出一个包含大量 TaskCanceledExceptions 的 AggregateException。

这意味着 WaitAll 会在两种不同的情况下抛出异常:

表示真正错误的异常。这意味着存在我们不知道如何处理的情况;它们需要作为未处理的异常传播,直到它们最终终止进程。 表示用户单击了取消按钮的异常。这意味着任务被取消并清理干净,程序应该继续正常运行。

后者完全符合vexing exception 的定义:它是在完全非异常情况下抛出的异常,因此我必须捕获它才能恢复正常的控制流。幸运的是,它很容易被抓住,对吧?只需添加 catch (AggregateException) 和 -- 哦等等,这与出现致命错误时抛出的类型相同。

我确实需要在返回之前等待任务完成运行(我需要知道他们不再使用他们的数据库连接、文件句柄或其他任何东西),所以我确实需要 WaitAll 或类似的东西.如果任何任务出现故障,我确实希望这些异常作为未处理的异常传播。我只是不希望取消例外。

如何防止WaitAll 为取消的任务抛出异常?

【问题讨论】:

使用接受 CancellationToken 的重载。 @HansPassant,该重载的文档实际上并没有说明它使用令牌的目的。它会忽略与该令牌关联的 TaskCanceledExceptions,还是会在令牌被取消时简单地提前返回?在所有任务停止运行之前,我需要它不要返回。 您使用 CancellationToken 取消任务。并过滤您现在将获得的特定 OperationCancelException。 【参考方案1】:

AggregateException 提供了可用于这些情况的Handle 方法。例如,如果您想忽略TaskCanceledException,您可以这样做:

var all = new AggregateException(
    new NullReferenceException(),
    new TaskCanceledException(),
    new TaskCanceledException(),
    new InvalidOperationException(),
    new TaskCanceledException());

try

    throw all;

catch (AggregateException errors)

    errors.Handle(e => e is TaskCanceledException);
 

如果所有异常都是TaskCanceledException类型,Handle方法不会抛出任何异常;否则将抛出仅包含未处理异常的新AggregateException

【讨论】:

【参考方案2】:

基于João Angelo's suggestion,这里有一个Task类扩展

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MySharedLibrary.Extensions

    public static class TaskExtensions
    

        // This code is based João Angelo's *** suggestion https://***.com/a/8681687/378115

        // Use this when a CancellationTokenSource is used
        public static void SafeWait(this Task TargetTask, CancellationTokenSource TargetTaskCancellationTokenSource)
        
            if (TargetTaskCancellationTokenSource.IsCancellationRequested == false)
            
                TargetTaskCancellationTokenSource.Cancel();
            
            SafeWait(TargetTask);
        

        // Use this when no CancellationTokenSource is used
        public static void SafeWait(this Task TargetTask)
        
            try
            
                if (TargetTask.IsCanceled == false)
                
                    TargetTask.Wait();
                
            
            catch (AggregateException errors)
            
                errors.Handle(e => e is TaskCanceledException);
            
        

    

【讨论】:

以上是关于如何在不抛出 TaskCanceledExceptions 的情况下等待任务?的主要内容,如果未能解决你的问题,请参考以下文章

我可以在不抛出异常的情况下测试正则表达式在 C# 中是不是有效吗

我迁移到空安全,我无法运行“flutter pub run build_runner build”而不抛出错误

如何不抛出一般指定的异常?

如何从子方法中退出方法而不抛出异常

如何在运行时检查权限而不抛出 SecurityException?

Kotlin vararg:如何将 null 设置为不抛出 NullPointerException 的 vararg?