【问题标题】:Waiting for multiple tasks when some might be cancelled当一些任务可能被取消时等待多个任务
【发布时间】:2021-02-02 13:04:27
【问题描述】:

我正在尝试等待多个任务,我预计其中一些可能会被取消。我使用this 问题作为灵感来源,让WhenAll 相应地处理取消的任务...

但是,如果没有任何任务被取消,这反过来也会引发异常!

var source = new CancellationTokenSource();

var task1 = Task.Delay(100, source.Token);
source.Cancel();
var task2 = Task.Delay(300);

await Task.WhenAll(task1, task2).ContinueWith(_ => { }, TaskContinuationOptions.OnlyOnCanceled);

在上面的例子中,如果source.Cancel() 没有被执行,那么最终的await 将会抛出一个异常。我可以通过从最后一行中删除 ContinueWith 来解决这个问题,但是如果任务被取消,WhenAll 将失败并出现同样的错误。

当其中一些任务可能(但不是必须)被取消时,等待任务列表的正确做法是什么?

【问题讨论】:

    标签: c# async-await cancellation


    【解决方案1】:

    ContinueWith 是一个primitive 方法,一般不适合应用程序代码。将其与await 混合使用更不可取,因为您使用两种语义略有不同的不同机制来实现相同的目标。您的示例意味着您要忽略由取消 CancellationTokenSource 引起的异常。我的建议是 catch 这些异常并忽略它们。

    var cts = new CancellationTokenSource(100);
    
    var task1 = Task.Delay(200, cts.Token);
    var task2 = Task.Delay(300);
    
    try
    {
        await Task.WhenAll(task1, task2);
    }
    catch (OperationCanceledException) {  } // Ignore cancellation exceptions
    

    您可以使用 when 上下文关键字更加具体,以避免忽略由未知 CancellationTokenSources 引发的异常:

    catch (OperationCanceledException ex) when (ex.CancellationToken == cts.Token) { }
    

    这可能会产生不同的问题,因为方法可能会将提供的 CancellationToken 包装到 linked CancellationTokenSource 中,在这种情况下,catch/when 可能无法处理源自的 OperationCanceledException通过一个已知的令牌。

    【讨论】:

    • 不确定我是否真的会使用您的建议,但答案本身非常有用。谢谢!
    • 附带说明 - 如果 ContinueWith 不明智......那么如果您需要在代码中动态添加“在任务 Y 之后执行操作 X”类型的逻辑,还有什么替代方案?这里的关键字是“动态”...
    • @Shaamaan "在任务 Y 之后执行动作 X" 可以实现为 await Y; X();。如果您想有条件地执行动作 X,则将此代码放入 async void 方法中,并有条件地调用此方法。将其设为async void 将确保此方法中的任何未处理异常都将浮出水面(通常是通过使进程崩溃)。
    【解决方案2】:

    我认为问题在于您传递的TaskContinuationOption。在您的示例中,您使用OnlyOnCanceled。所以只有在任务被取消时才会继续。

    我不确定取消任务时所需的行为是什么。如果您只想在其中没有一个被取消时继续,您可以使用NotOnCanceled。如果您想在任何一种情况下继续,无论是否取消任务,那么您可以使用NotOnFaulted,因为取消不被视为错误。

    【讨论】:

    • 我的代码基于链接提供的问题/答案,但您是对的!它应该是NotOnFaulted,而不是NotOnCanceled。谢谢!
    • aah,我不确定行为是否应该相同,例如全部完成或取消后继续进行:)
    猜你喜欢
    • 1970-01-01
    • 2016-06-04
    • 2016-10-15
    • 1970-01-01
    • 2020-07-20
    • 1970-01-01
    • 2017-10-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多