链接两个 CancellationToken,无需冗余 CancellationTokenSource

bit*_*onk 1 c# task-parallel-library cancellationtokensource .net-core

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

AFAIK,这样做的方法是使用CreateLinkedCancellationTokenSource. 但它的 API 似乎相当不舒服,因为我需要为此创建两个额外的实例,并且因为它们实现了,所以我也必须不要忘记处置它们。因此,我需要将它们都存储为成员以供以后处理。CancellationTokenSourceIDisposable

我错过了什么吗?我觉得应该有一种更简单的方法来将额外的取消机制附加到现有令牌上,而不会迫使我维护两个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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 5

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

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,我会仔细考虑这种 API 设计的生命周期问题。当前StartAsync进行资源分配和AbortAsync清理;我推荐一种由构造函数和(RAII)处理这些的设计Dispose

  • [`CancellationTokenSource.CreateLinkedTokenSource`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource.createlinkedtokensource) 方法有两个重载,一个重载有两个令牌作为参数,第二个重载有两个重载带有“params”标记数组。令人惊讶的是,似乎最有用的接受单个令牌的重载丢失了。难怪有些人(包括我自己)心中形成了“链接源至少需要两个令牌”的误解。 (3认同)