在没有冗余 CancellationTokenSources 的情况下链接两个 CancellationTokens

Linking two CancellationTokens without redundant CancellationTokenSources

在获取 CancellationToken (StartAsync) 的方法中,我想添加一个内部 CancellationToken 以便调用者可以在外部或内部取消异步操作 (例如,通过调用 AbortAsync() 方法)。

AFAIK,方法是使用 CreateLinkedCancellationTokenSource。但它的 API 似乎相当不舒服,因为我需要为此创建 two 个额外的 CancellationTokenSource 实例,并且因为它们实现了 IDisposable,我也不能忘记处理他们。因此,我需要将它们都存储为成员以备后用。

我错过了什么吗?我觉得应该有一种更简单的方法来将额外的取消机制附加到现有令牌上,而不是强迫我维护两个 CancellationTokenSource 个实例。

public Task StartAsync(CancellationToken externalToken)
{
    this.actualCancellation = new CancellationTokenSource();
    this.linkedCancellation = CancellationTokenSource.CreateLinkedTokenSource(
        actualCancellation.Token, externalToken);
    this.execution = this.ExecuteAsync(this.linkedCancellation.Token);
    return this.execution;
}

public async Task AbortAsync()
{
    try
    {
        this.actualCancellation.Cancel();
        await this.execution;
    }
    catch
    {
    }
    finally
    {
        this.actualCancellation.Dispose();
        this.linkedCancellation.Dispose();
    }
}

链接取消源不是一种特殊的取消源。它是一个常规取消源,也是 "linked" 到现有令牌 - 即,当现有令牌被取消时,源将被取消。在所有其他方面,它是一个正常的取消源,因此您可以像任何其他取消源一样自行取消它。

因此您只需要一个取消源 - 链接到现有令牌的一个 也可以手动取消:

public Task StartAsync(CancellationToken externalToken)
{
    this.linkedCancellation = CancellationTokenSource.CreateLinkedTokenSource(externalToken);
    this.execution = this.ExecuteAsync(this.linkedCancellation.Token);
    return this.execution;
}

public async Task AbortAsync()
{
    try
    {
        this.linkedCancellation.Cancel();
        await this.execution;
    }
    catch
    {
    }
    finally
    {
        this.linkedCancellation.Dipose();
    }
}

作为旁注,我会仔细考虑这种 API 设计的生命周期问题。目前 StartAsync 负责资源分配,AbortAsync 负责清理;我推荐一种设计,其中这些由构造函数和 Dispose (RAII).

处理