c# - 为什么合作取消任务的状态是 "Faulted"而不是 "Canceled"?

标签 c# .net

我的问题是基于这个 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”,这很好。

根据问题的答案,当我们将 token 传递给任务的构造函数或运行的参数时,t 的状态应该是“已取消”,因为

when the task sees that OperationCanceledException, it checks whether the OperationCanceledException's token matches the Task's token. If it does, that exception is viewed as an acknowledgement of cooperative cancellation and the Task transitions to the Canceled state (rather than the Faulted state)

但即使我们将 token 传递为

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”,所以我误解了问题的答案还是我在代码中做错了什么?

最佳答案

任务状态为 Faulted,因为该方法在您的代码有机会取消它之前抛出 OverflowException。如果您检查 Task.Exception.InnerException 属性,您会发现它是一个带有此消息的 OverflowException:

Arithmetic operation resulted in an overflow.

这是因为 500 毫秒对于运行一个只做一点点数学运算的循环来说是非常的时间。并且您的循环使用 checked 算法来执行求和,并且 1 到 100,000 之间的数字的总和刚刚超过 50 亿,比 int 可以做的大得多实际上持有。

在主线程取消 token 所需的 500 毫秒之前,该方法很容易达到溢出。

同样,如果您删除 checked,任务将运行完成,然后您才能取消它。

如果你想看到取消,在Sum()方法的开头添加对Thread.Sleep(750)的调用,这样它就不会' t 检查取消标记,直到主线程有机会取消它。如果这样做,您会发现任务处于您预期的 Canceled 状态。

关于c# - 为什么合作取消任务的状态是 "Faulted"而不是 "Canceled"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67630574/

相关文章:

c# - 我可以为 C# 正则表达式定义自定义模式吗?

c# - FontSize.PIXELS c# 等价物

c# - 使用C#在Windows 7中模拟鼠标点击和鼠标移动

c# - ProtectedData.用户更改密码后取消保护

c# - 有没有办法自定义 OpenFileDialog 来选择文件夹而不是文件?

c# - Umbraco MediaService/Umbraco MediaItem 未保存

.Net SvcUtil : attributes must be optional

.net - 在 TIBCO EMS(或其他 JMS)中,如何创建可扩展的请求/响应处理器?

c# - 将 Unity App 连接到 mySQL 数据库

c# - 相当于 FromQuery 属性的 Razor