【问题标题】:Task cancellation, when?任务取消,什么时候?
【发布时间】:2015-12-15 19:10:32
【问题描述】:

对于我正在制作的 API 的实现,我希望用户能够取消他们的任务(任务)。我发现我需要一个CancellationTokenSource,所以我可以从前者创建一个CancellationToken。现在我正在处理取消请求 (IsCancellationRequested/ThrowIfCancellationRequested) 我有点困惑。检查/执行此操作的合适时间是什么时候?

例如(虚构):

async Task<int> DoStuff(int number, CancellationToken token) {
    // 1. Here, callee-site?
    token.ThrowIfCancellationRequested();

    var resultTask1 = _database.GetSomeDataFromDatabase(token); // 2. Inside this method?
    var resultTask2 = _service.SomeRestCall(token); // 2. Inside this?

    // 3. Here, before the tasks return?
    token.ThrowIfCancellationRequested();
    var result1 = await resultTask1;
    var result2 = await resultTask2;

    // 4. In memory processing, here?
    foreach(var item in result1)
    {
        // ...
    }

    foreach(var item in result2)
    {
        // 1. Here?
        token.ThrowIfCancellationRequested();
        await _fileSystem.Save(fileName, item, token); // 5. Inside this?
    }

    // 6. Here probably doesn't make sense though, the result is already retrieved?
    return 123;
}

取消任务的最佳做法是什么?

【问题讨论】:

  • 我认为如果任务很小,您可以在返回之前进行取消检查。如果任务很长,您可以放在多个位置(如果取消,请注意回滚)。最后,如果有一个长循环,你可以把取消检查放在循环内。这只是我的意见。更聪明的人可能会有一个合理和更好的答案。
  • @displayName:为什么?那只是一种错觉。一切都已保存。
  • @jgauffin:尽管我明确表示这是我的观点并且人们可能更聪明,但我仍然有人质疑我我的拙见。 :) 无论如何.. 我更新的评论更好吗?
  • @displayName:我不是故意冒犯你的。 a) 我只是想为 OP 指出它 b) 还想看看我是否遗漏了什么
  • @jgauffin: 1) 没有冒犯。 2)感谢您,我们有更好的评论和更好的答案。

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


【解决方案1】:

检查/执行此操作的合适时间是什么时候?

对于受 CPU 限制的方法,定期调用 CancellationToken.ThrowIfCancellationRequested 是合适的。例如,如果您有一个紧密的处理循环,您可能希望每隔几百次迭代调用一次。

但是,听起来您的情况都是 I/O 绑定的(自然是异步的)。在这种情况下,最好将令牌向下传递。更高级别的异步方法可以只传递令牌。在最底层,如果 API 不直接支持CancellationToken,那么一般最好使用CancellationToken.Register 来实现异步操作的真正取消。当然,如果最底层的API确实直接支持CancellationToken,那就直接通过就好了。

【讨论】:

    【解决方案2】:

    取消通常在等待需要时间的操作时完成。例如等待 IO 操作或进行基于 CPU 的计算。

    在您的示例中,实际上没有必要在 #1、#2 和 #6 处取消,因为它们会立即闪过。

    但是,如果列表很大或计算需要时间,则检查 #4 中的取消是有意义的。

    最后 #5 是赢家,因为 IO 并不快。但是,_fileSystem.Save 不会抛出TaskCancelledException 吗?在那种情况下,它已经被处理了。

    请注意,通过取消令牌,您也可以说事情是可以恢复的。如果没有,请在文档中明确说明取消只会中止那些尚未保存的计算。

    【讨论】:

      【解决方案3】:

      没有什么叫做“最佳实践”。在不同的场景中,它的使用可能会有所不同。

      基本上,ThrowIfCancellationRequested() 用于防止在询问取消时运行任何额外的代码行。把它想象成下面的类型检查:

      if (token.IsCancellationRequested) 
          throw new OperationCanceledException(token); // break the processing immediately
      

      现在由您决定在请求取消时应该停止执行哪个代码段。正如@Jgauffin 所建议的那样, LongRunning 代码部分将是进行此类检查的好地方。此外,如果您有多个区域,例如在给定的示例 2 for 循环中,您可以同时使用这两个循环以避免在要求取消后进行进一步处理。

      【讨论】:

        猜你喜欢
        • 2019-10-13
        • 2022-10-07
        • 2012-07-25
        • 2017-01-26
        • 1970-01-01
        • 2015-05-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多