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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With