From what I've read about Tasks, the following code should cancel the currently executing task without throwing an exception. I was under the impression that the whole point of task cancellation was to politely "ask" the task to stop without aborting threads.
The output from the following program is:
Dumping exception
[OperationCanceledException]
Cancelling and returning last calculated prime.
I am trying to avoid any exceptions when cancelling. How can I accomplish this?
void Main() { var cancellationToken = new CancellationTokenSource(); var task = new Task<int>(() => { return CalculatePrime(cancellationToken.Token, 10000); }, cancellationToken.Token); try { task.Start(); Thread.Sleep(100); cancellationToken.Cancel(); task.Wait(cancellationToken.Token); } catch (Exception e) { Console.WriteLine("Dumping exception"); e.Dump(); } } int CalculatePrime(CancellationToken cancelToken, object digits) { int factor; int lastPrime = 0; int c = (int)digits; for (int num = 2; num < c; num++) { bool isprime = true; factor = 0; if (cancelToken.IsCancellationRequested) { Console.WriteLine ("Cancelling and returning last calculated prime."); //cancelToken.ThrowIfCancellationRequested(); return lastPrime; } // see if num is evenly divisible for (int i = 2; i <= num/2; i++) { if ((num % i) == 0) { // num is evenly divisible -- not prime isprime = false; factor = i; } } if (isprime) { lastPrime = num; } } return lastPrime; }
There's 2 likely reasons that a TaskCanceledException would be thrown: Something called Cancel() on the CancellationTokenSource associated with the cancellation token before the task completed. The request timed out, i.e. didn't complete within the timespan you specified on HttpClient. Timeout .
An exception thrown by this operation represents cancellation only when its type inherits from OperationCanceledException and when the CancellationToken. IsCancellationRequested property is true .
The wait handle of the cancellation token will become signaled in response to a cancellation request, and the method can use the return value of the WaitAny method to determine whether it was the cancellation token that signaled. The operation can then just exit, or throw a OperationCanceledException, as appropriate.
I am trying to avoid any exceptions when cancelling.
You shouldn't do that.
Throwing OperationCanceledException
is the idiomatic way that "the method you called was cancelled" is expressed in TPL. Don't fight against that - just expect it.
It's a good thing, because it means that when you've got multiple operations using the same cancellation token, you don't need to pepper your code at every level with checks to see whether or not the method you've just called has actually completed normally or whether it's returned due to cancellation. You could use CancellationToken.IsCancellationRequested
everywhere, but it'll make your code a lot less elegant in the long run.
Note that there are two pieces of code in your example which are throwing an exception - one within the task itself:
cancelToken.ThrowIfCancellationRequested()
and one where you wait for the task to complete:
task.Wait(cancellationToken.Token);
I don't think you really want to be passing the cancellation token into the task.Wait
call, to be honest... that allows other code to cancel your waiting. Given that you know you've just cancelled that token, it's pointless - it's bound to throw an exception, whether the task has actually noticed the cancellation yet or not. Options:
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