使用ContinueWith如何处理任务取消?

4

我有以下示例:

public void Run()
{
    var ctc = new CancellationTokenSource();

    try
    {
        DoAsync(ctc).Wait();

        Console.WriteLine("Done");
    }
    catch (AggregateException exception)
    {
        Console.WriteLine("Inside try-catch block");
        Console.WriteLine();
        Console.WriteLine(exception);

        exception.Handle(ex =>
        {
            Console.WriteLine(ex.Message);
            return true;
        });
    }
}

private async Task DoAsync(CancellationTokenSource ctc)
{
    Console.WriteLine("DoAsync started");

    await Task.Run(() =>
                Console.WriteLine("DoAsync Run"),
                ctc.Token
            )
            .ContinueWith(antecedent =>
                Console.WriteLine("DoAsync Run cancelled"),
                TaskContinuationOptions.OnlyOnCanceled
            );

    Console.WriteLine("DoAsync finished");
}

我已经创建了一个方法 (DoAsync),可以执行一些异步工作,并且可以在任何时候取消。如您所见,Task.Run获取了一个取消令牌。因此,我使用continuationOptions = TaskContinuationOptions.OnlyOnCanceled创建了连续任务。结果,我期望只有在请求取消时才调用连续任务,在其他情况下被忽略。但是在我的实现中,由ContinueWith返回的任务在其前置任务未被取消时会抛出异常:
DoAsync started
DoAsync Run

Inside try-catch block
System.AggregateException...

A task was canceled.

我可以通过添加另一个 ContinueWith 来修复这个问题,如下例所示:

await Task.Run(() =>
        Console.WriteLine("DoAsync Run"),
        ctc.Token
    )
    .ContinueWith(antecedent =>
        Console.WriteLine("DoAsync Run cancelled"),
        TaskContinuationOptions.OnlyOnCanceled
    )
    .ContinueWith(antecedent => { });

这段代码不会抛出任何异常。

但是我能够正确地使用单个ContinueWith处理取消吗?


你尝试过只传递令牌而不是令牌源吗? - Fildor
1
@Fildor,我同意应该传递令牌而不是令牌源。但这怎么解决我的问题呢? - Vladyslav Yefremov
如果你正在使用await,为什么还要使用ContinueWith?await的整个意义在于,让你在任务完成后直接继续下一行执行接下来的操作。我有什么地方理解错了吗? - AlexDev
1
你为什么要同时使用async/await和continue with?你可以用try/catch包裹await并捕获catch (OperationCanceledException ex) when ex.CancellationToken.Equals(ctc.Token) { Console.WriteLine("DoAsync Run cancelled"); } 来重新创建它。 - Scott Chamberlain
1
@VladyslavYefremov:你根本不应该使用ContinueWith。在try/catch中使用await会导致代码更易于维护。 - Stephen Cleary
显示剩余2条评论
1个回答

4

ContinueWith的注释中明确说明:

如果通过continuationOptions参数指定的继续条件未满足,继续任务将被取消而不是调度。

由于您为前置条件指定的条件未满足(即它未被取消),因此继续操作被设置为取消。您等待了已取消的任务,因此导致DoAsync出现了操作取消异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接