I had a conversation with Stephen Toub, Peter Ritchie and Kevin Hazzard about the different between the TaskCancelledException and the OperationCancelledException. It turns out not to be a big difference, but some understanding might keep you out of a bit of trouble.
TaskCancelledException derives from OperationCancelledException. TaskCancelledException contains a property for the cancelled task.
If you’re catching exceptions, you probably want to catch both exceptions. If you don’t care about the task, you can do this by just catching the OperationCancelledException since the TaskCancelledException derives from it. Or you can catch them both in order if you use the task in your exception response. Catching only the TaskCancelledException will not work in all cases, since it won’t catch OperationCancelledException.
If you’re throwing an exception, and there’s a task available, throw TaskCanceledException. If there isn’t a task available, throw an OperationCancelledException. This is reflected in the framework when the CancellationToken’s ThrowIfCancellationRequested method throws an OperationCancelledException.
If you’re manually throwing cancellation exceptions, check that you aren’t repeating things already handled well by the .NET framework. CancellationTokenSource and CancellationToken may take a little getting used to, but this mechanism provides a predictable approach that handles some tricky threading side cases that will ultimately make your code simpler and easier to understand.
Throwing exceptions to communicate cancellation steps slightly outside usual exception guidelines. Exceptions are intended for exceptional circumstances, and cancellation is arguably not an exceptional occurrence. But, one of the things exceptions do is abort further processing and fail to return a value. Clearly this should happen on cancellation. I think the tradeoff is worthwhile because treating cancellations as exceptions significantly simplifies your code and avoids bogus return values after cancellation.
One of the reasons exceptions are bad for implementing control flow outside exceptional circumstances is that there must be no confusion about the exceptions that are thrown. Let’s avoid confusion J
- Catch OperationCancelledException, and possibly also TaskCancelledException
- Throw TaskCancelledException when you have a task to pass, otherwise OperationCancelledException
Thanks to Stephen, Peter and Kevin – I’m a bit smarter than I was a few hours ago.