【问题标题】:Ensuring the Cancellation of some Task确保取消某些任务
【发布时间】:2017-03-08 20:41:02
【问题描述】:

假设我想确保从异步方法返回的任务对象由于调用者的取消请求而转换为取消状态。

关键是:无论上述方法使用的异步方法如何实现以及它们是否完成,都应该发生这种情况。

考虑以下扩展方法:

    public static Task<T> ToTask<T>(this CancellationToken cancellationToken)
    {
        var tcs = new TaskCompletionSource<T>();
        cancellationToken.Register(() => { tcs.SetCanceled(); });
        return tcs.Task;
    }

我现在可以使用这样的任务来确保上述场景:

 public async Task<Item> ProvideItemAsync(CancellationToken cancellationToken)
    {
        Task<Item> cancellationTask = cancellationToken.ToTask<Item>();

        Task<Item> itemTask = _itemProvider.ProvideItemAsync(cancellationToken);

        Task<Task<Item>> compoundTask = Task.WhenAny(cancellationTask, itemTask);

        Task<Item> finishedTask = await compoundTask;

        return await finishedTask;
    }

我的问题是:

1) 这种方法有什么问题吗?
2) 是否有内置 API 来促进这样的用例

谢谢!

【问题讨论】:

  • rawImageTask 还是itemTaskitemTask 当前是未使用的本地。
  • @spender 抱歉,那个人逃过了编辑。
  • “不管实际操作如何实现”是不可能的。任务取消是一个合作过程;如果任务在被取消时没有检查任何地方,则它实际上不会被取消。当然,在这种情况下,您仍然可以随意忽略结果并它被取消了——但它会继续在后台运行。
  • @JeroenMostert。你是对的,但我可以确保的是,客户的取消请求不会被完全忽略,这有时是至关重要的。我已经修改了我的问题以进一步说明我的意图。
  • 您的ToTask 方法存在问题,这对于使用Register 的任何方法都很常见;这会泄漏资源。有关更多信息(以及一些建议的解决方案),请参阅this question。包括指向this 的链接,这似乎实现了您所追求的。

标签: c# asynchronous task-parallel-library cancellation


【解决方案1】:

假设我要确保取消一个异步操作, 不管实际操作是如何实现的,不管它是否完成。

除非将代码包装到单独的进程中,否则这是不可能的。

当我说“确保”时,我的意思是说表示所述操作的任务转换到取消状态。

如果您只想取消 任务(而不是操作本身),那么当然可以。

这种方法有什么问题吗?

这里有一些棘手的边缘情况。特别是,如果任务成功完成,您需要处置Register的结果。

我建议在我的AsyncEx.Tasks library 中使用WaitAsync extension method

public Task<Item> ProvideItemAsync(CancellationToken cancellationToken)
{
  return _itemProvider.ProvideItemAsync(cancellationToken).WaitAsync(cancellationToken);
}

【讨论】:

  • 不幸的是,我无法在该程序集级别承担依赖关系。但是,我会了解您的实现并使用这些知识来解决手头的问题。顺便说一句,如果我可以说 - 你的工作在我的脑海中激发了很多想法。谢谢。
  • 如果我可能会问,在你的 DoWaitAsyncMethod 的实现中,你为什么用一个 false 参数调用 'ConfigureAwait' 方法?它的用途是什么?
  • @EyalPerry:我在我的intro to async blog post 和我的async best practices article 中解释了这一点。
  • 我之前已经阅读了这两篇文章,并且我了解 ConfigureAwait(false) 的作用,我将重新提出我的问题:通过调用 ConfigureAwait(false),您会导致任务实例在不同的同步上下文中恢复比原来的 - 使用此扩展方法的代码不一定“预期” - 我完全同意你建议的做法是一个好的做法,我只是无法设想一个决策过程证明在实用程序级别上这样做是合理的比如上述的扩展方法。我希望我没有“超越”,我非常尊重所有这一切。
  • @EyalPerry:每个await 都会捕获自己的上下文。所以决定是在本地做出的:WaitAsync 方法是否需要在特定的上下文中恢复?不,所以使用ConfigureAwait(false)调用者是否需要由调用者处理。
猜你喜欢
  • 2012-05-27
  • 1970-01-01
  • 2018-02-22
  • 2020-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多