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