任务取消,什么时候?

时间:2015-12-15 19:10:32

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

对于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;
}

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

3 个答案:

答案 0 :(得分:4)

  

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

对于CPU绑定方法,定期调用CancellationToken.ThrowIfCancellationRequested是合适的。例如,如果你有一个严格的处理循环,你可能想要每隔几百次迭代调用它。

但是,听起来你的情况都是I / O绑定的(自然是异步的)。在这种情况下,最好只传递令牌。更高级别的异步方法只能传递令牌。在最低级别,如果API不直接支持CancellationToken,那么通常最好使用CancellationToken.Register来实现异步操作的真正取消。当然,如果最低级别的API直接支持CancellationToken,那么只需通过它。

答案 1 :(得分:3)

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

在你的例子中,确实没有必要在#1,#2和#6取消,因为它们只是毫不拖延地完成。

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

最后#5是赢家,因为IO不快。但是,_fileSystem.Save不会TaskCancelledException投掷?在那种情况下,它已经被照顾。

请注意,通过取消令牌,您还可以说事情可以恢复。如果没有,请在文档中清楚地表明取消将中止那些尚未保存的计算。

答案 2 :(得分:0)

没有什么叫做#34; Best-Practice&#34;。在不同情况下,它的使用可能会有所不同。

基本上ThrowIfCancellationRequested()用于防止在提出取消时运行任何额外的代码行。可以将其视为以下类型检查:

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

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