Task.Run 和 CancellationToken。在这种情况下取​​消是如何工作的?

Task.Run and CancellationToken. How does cancellation work in this scenario?

为什么我们在接下来的 2 个场景中有 2 个不同的结果?在这些样本中 token.IsCancellationRequested = 正确。如果我评论 if(token.IsCancellationRequested),为什么功能会发生变化,而实际上我们可以键入 if(true) 而不是它。

我在某处读到,为了获得 TaskCanceledException(Status = Canceled),我们需要遵循接下来的 3 个条件:

  1. 抛出 OperationCanceledException 并将令牌传递给它的构造函数
  2. 将相同的令牌传递给创建任务的方法 (Task.Run)
  3. IsCancellationRequested 应该为真。

确实如此,如果我们使用 if(token.IsCancellationRequested) throw new OperationCanceledException(token) (等于 token.ThrowIfCancellationRequested)(第二种情况),我们需要遵循其他 2 个条件,但在第一种情况,如果我们评论 if(token.IsCancellationRequested)(我们甚至可以从 CTS 构造函数中删除 1),我们仍然会获得 Canceled 状态。

1.这里的状态是 Canceled

CancellationTokenSource cts = new CancellationTokenSource(1);
        var token = cts.Token;
        var task = Task.Run(() =>
        {
                Thread.Sleep(5);
                // if(token.IsCancellationRequested)
                throw new OperationCanceledException(token);
        });

        Thread.Sleep(50);
        Console.WriteLine(task.Status);

2。这里 - 故障。

CancellationTokenSource cts = new CancellationTokenSource(1);
        var token = cts.Token;
        var task = Task.Run(() =>
        {
            Thread.Sleep(5); 
            if(token.IsCancellationRequested) // true
                throw new OperationCanceledException(token);
        });

        Thread.Sleep(50);
        Console.WriteLine(task.Status);

似乎如果编译器能够在编译时知道任务将始终抛出 OperationCanceledException 将以 Canceled 状态结束,否则它将以 Faulted 结束。

void Main()
{
    CancellationTokenSource cts = new CancellationTokenSource(1);
    cts.Cancel();
    var token = cts.Token;
    var task = Task.Run(() =>
    {
        //if(GetTrue())  //Faulted
        if(true)         //Canceled
            throw new OperationCanceledException();
    });
    
    Thread.Sleep(50);
    Console.WriteLine(task.Status);
}

bool GetTrue()
{
    return true;
}

但是,如果您将相同的令牌发送到 Task.Run,因为您将其放入任务中,它将以 Canceled 结束。

void Main()
{
    CancellationTokenSource cts = new CancellationTokenSource(10000);
    var token = cts.Token;
    var task = Task.Run(() =>
    {
        cts.Cancel();
        token.ThrowIfCancellationRequested();
    }, token);
    
    Thread.Sleep(50);
    Console.WriteLine(task.Status);
}