My question is based on the answer to this question.
Let's say we have the following code:
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;
}
}
We can see that t
's status is Faulted
which is good.
According to the answer to the question, when we pass the token into task's constructor or Run's parameter, t
's status should be "Canceled" because
when the task sees that
OperationCanceledException
, it checks whether theOperationCanceledException
'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)
but even if we pass the token as
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
's status is still Faulted
not Canceled
, so did I misunderstand the answer to the question or did I do something wrong in the code?
The task status is Faulted
because the method throws an OverflowException
before your code gets a chance to cancel it. If you check the Task.Exception.InnerException
property, you'll find it's an OverflowException
with this message:
Arithmetic operation resulted in an overflow.
That's because 500 ms is a very long time when it comes to running a loop that's just doing a tiny bit of math. And your loop uses checked
arithmetic to perform the sum, and the sum of the numbers between 1 and 100,000 is just a bit over 5 billion, much larger than what an int
can actually hold.
The method easily reaches the overflow before the 500 ms it takes for your main thread to cancel the token.
Similarly, if you remove the checked
, the task will run to completion before you can cancel it.
If you want to see the cancellation, add a call to Thread.Sleep(750)
at the beginning of the Sum()
method, so that it won't check the cancel token until the main thread has had a chance to cancel it. If you do that, then you will find the task in the Canceled
state as you expected it to be.