.net 为什么在协同取消中任务的状态是Faulted而不是Canceled?

nfeuvbwi  于 2022-12-30  发布在  .NET
关注(0)|答案(1)|浏览(181)

我的问题基于this question的答案。
假设我们有以下代码:

class Program 
{
    static void Main(string[] args) 
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        Task<Int32> t = Task.Run(() => Sum(cts.Token, 100000));     
        
        // make sure the the task run first before 
        Thread.Sleep(500);   
        cancellation request occurs
        cts.Cancel();
      
        var status = t.Status;  // status is "Faulted"
        Console.ReadLine();
    }

    static Int32 Sum(CancellationToken ct, Int32 n) 
    {
        Int32 sum = 0;
        for (; n > 0; n--) 
        {
            // throws
            ct.ThrowIfCancellationRequested();    

            OperationCanceledException if cancellation request occurs
            checked { sum += n; } 
        }
        return sum;
    }
}

我们可以看到t的状态是Faulted,这是好的。
根据问题的答案,当我们将令牌传递给任务的构造函数或Run的参数时,t的状态应为“Canceled”,因为
当任务看到OperationCanceledException时,它检查OperationCanceledException的令牌是否与任务的令牌匹配。如果匹配,则该异常被视为协作取消的确认,并且任务转换到已取消状态(而不是故障状态)
但即使我们将令牌传递为

class Program 
{
    static void Main(string[] args) 
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        
        // pass the token into Run() method as the second parameter
        Task<Int32> t = Task.Run(() => Sum(cts.Token, 100000), cts.Token);     
        Thread.Sleep(500);
        cts.Cancel();

        // status is still "Faulted", not "Canceled"
        var status = t.Status;  
        Console.ReadLine();
    }
    //...
}

t的状态仍然是Faulted而不是Canceled,那么我是误解了问题的答案还是在代码中做错了什么?

sdnqo3pr

sdnqo3pr1#

任务状态为Faulted,因为在代码有机会取消它之前,该方法抛出了一个OverflowException。如果检查Task.Exception.InnerException属性,您会发现它是一个OverflowException,并显示以下消息:
算术运算导致溢出。
这是因为500 ms对于运行一个只做一点点数学运算的循环来说是一个"非常"长的时间,而且你的循环使用checked算法来执行求和,1到100,000之间的数字之和刚刚超过50亿,比int实际所能容纳的要大得多。
该方法很容易在主线程取消标记所需的500毫秒之前达到溢出。
同样,如果删除checked,则任务将运行到完成,然后才能取消它。
如果你想看到取消,在Sum()方法的开头添加一个对Thread.Sleep(750)的调用,这样它就不会检查cancel标记,直到主线程有机会取消它。如果你这样做,你会发现任务处于Canceled状态,正如你所期望的那样。

相关问题