【问题标题】:Linking two CancellationTokens without redundant CancellationTokenSources在没有冗余 CancellationTokenSource 的情况下链接两个 CancellationToken
【发布时间】:2020-05-04 10:41:36
【问题描述】:

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

AFAIK,方法是使用CreateLinkedCancellationTokenSource。但它的 API 似乎相当不舒服,因为我需要为此创建 两个 额外的 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();
    }
}

【问题讨论】:

  • 附带说明,处理CancellationTokenSource 可能不是绝对关键。 Here 是我对此的看法。有人报告memory leaks是由未处理的CTS引起的,但我没有设法重现它。

标签: c# .net-core task-parallel-library cancellationtokensource


【解决方案1】:

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

所以你只需要一个取消源——一个链接到现有令牌并且也可以手动取消:

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) 处理。

【讨论】:

  • 有一次我的代码和你的完全一样,但我不知何故认为它不起作用。谢谢!
  • CancellationTokenSource.CreateLinkedTokenSource 方法有两个重载,一个带有两个标记作为参数,另一个带有 params 标记数组。令人惊讶的是,似乎最有用的接受单个令牌的重载却不见了。难怪有些人(包括我自己)心中形成了“一个链接的源至少需要两个令牌”的误解。
  • @TheodorZoulias 显然他们已经意识到了这个错误,并在 .NET 5 docs.microsoft.com/en-us/dotnet/api/… 中添加了另一个重载
猜你喜欢
  • 2022-01-02
  • 2017-11-16
  • 2018-03-24
  • 2021-03-28
  • 2013-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多