异步延续的取消确认如何工作?

问题描述 投票:0回答:1

Task.IsCanceled

文档指定
OperationCanceledException.CancellationToken
必须与用于启动任务的取消令牌匹配才能正确确认。我对异步延续中如何工作感到困惑。似乎有一些未记录的行为允许他们在不提前知道取消令牌的情况下进行确认。

static void TestCancelSync()
{
    // Create a CTS for an already running task.
    using var cts = new CancellationTokenSource();
    cts.Cancel();

    // Attempt to acknowledge cancellation of the CT (doesn't work).
    cts.Token.ThrowIfCancellationRequested();
}

static async Task TestCancelAsync()
{
    await Task.Yield();

    // Create a CTS for an already running continuation.
    using var cts = new CancellationTokenSource();
    cts.Cancel();

    // Acknowledge cancellation of the CT (how is this possible?).
    cts.Token.ThrowIfCancellationRequested();
}

var syncCancelTask = Task.Run(TestCancelSync);
var asyncCancelTask = TestCancelAsync();

try
{
    await Task.WhenAll(syncCancelTask, asyncCancelTask);
}
catch (OperationCanceledException)
{
    // Prints "{ syncCanceled = False, asyncCanceled = True }"
    Console.WriteLine(
        new
        {
            syncCanceled = syncCancelTask.IsCanceled,
            asyncCanceled = asyncCancelTask.IsCanceled,
        });
}

我想了解这是如何在幕后工作的。到底什么时候需要确认特定令牌的取消?我可以相信异步方法中的任何任务都会有这种未记录的行为吗?

c# .net async-await task-parallel-library cancellation-token
1个回答
0
投票

您似乎观察到的行为是任务取消和OperationCanceledException在.NET中的工作方式,特别是在异步延续的上下文中。

任务取消: 在 .NET 中,任务可以处于 3 个最终状态之一:

  • RanToComplete
  • 有故障
  • 取消

如果任务通过抛出链接到用于请求取消的 CancellationToken 的 OperationCanceledException 来确认取消请求,则该任务被视为已取消。

OperationCanceledException: 这包括一个属性 CancellationToken,指示哪个令牌触发了取消。任务基础结构检查此令牌是否与与任务的取消请求关联的令牌匹配。如果它们匹配,则任务将被标记为“已取消”,否则将被标记为“故障”。

您的第一个场景 TestCancelSync 不与与 CancellationTokenSource (CTS) 直接相关的任务进行交互。 ThrowIfCancellationRequested 方法会抛出 OperationCanceledException,但由于它不是在使用 cts 的令牌启动的任务上下文中抛出的,因此没有需要确认的任务取消。因此,运行此方法的任务不会被标记为已取消,而是正常完成或由于未处理的异常而出现故障。

对于第二个场景 TestCancelAsync,关键是理解“等待”和异步延续如何工作。当您在异步方法中等待某些内容时,编译器会将方法的其余部分转换为应在等待的操作完成后运行的延续。如果在异步方法中的等待之后抛出OperationCanceledException,则任务基础结构可以隐式地将此异常与正在等待的任务关联起来,即使取消标记未显式传递给方法或任务也是如此。您所遇到的情况(异步方法的任务被标记为已取消)是因为捕获了抛出的 OperationCanceledException 并将其视为取消异步方法返回的任务。这是无缝工作的,因为异步方法旨在通过观察异常并将任务转换为已取消状态来处理此异常。

你能相信这种行为吗?依赖隐式行为来取消任务可能会使你的代码不太清晰,如果逻辑变得更加复杂,则可能会引入错误。最好使用您正在使用的特定 CancellationToken 显式检查取消,并将令牌传递给支持它的所有异步操作。这样做可以使您的代码更具可读性,并确保取消行为是明确且可预测的。虽然您可以信任源自异步方法的任务的这种行为,但为了清晰、可维护性和可预测性,我建议您尽可能明确地处理取消。

© www.soinside.com 2019 - 2024. All rights reserved.