4

I am trying to migrate to using the CancellationToken/Src constructs in .NET, e.g.: https://learn.microsoft.com/en-us/dotnet/standard/threading/cancellation-in-managed-threads

My basic question is in regards to reacting to a cancellation request.

In my case I have some long-running processing that is not important for consistency. When a cancellation request arrives, I can't afford the pattern of "polling", e.g.:

        while (!_token.IsCancellationRequested)
        {
            DoProcessing(...)
        }

Because the processing can take several minutes and I really want to exit right now.

My question is if the proper method is then to simply use this setup:

    public void Start()
    {
        _token.Register(() => _token.ThrowIfCancellationRequested());

        // Continuously process stuff.
        while (!_token.IsCancellationRequested)
        {
            DoProcessing(...)
        }
    }

That is, register a callback which in turn just throws the OperationCanceledException.

I can read here: https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource.cancel?view=netframework-4.8 , that:

We recommend that cancelable operations and callbacks registered with CancellationToken not throw exceptions.

However...

What is the proper way of interrupting my processing immediately, rather than polling, and still adhering to the "rules" prescribed by the framework?

UmaN
  • 905
  • 1
  • 15
  • 29

1 Answers1

1

I can understand how that quote appears to recommend against using ThrowIfCancellationRequested, but that is not the case--using ThrowIfCancellationRequested is appropriate.

A couple of sources that indicate using ThrowIfCancellationRequested is appropriate:

This SO answer by Jon Skeet indicates that ThrowIfCancellationRequested is the correct approach.

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.

This Microsoft source indicates that ThrowIfCancellationRequested is the correct approach.

A successful cancellation involves the requesting code calling the CancellationTokenSource.Cancel method, and the user delegate terminating the operation in a timely manner. You can terminate the operation by using one of these options:

  • By simply returning from the delegate [snipped remainder of this bullet]
  • By throwing a OperationCanceledException and passing it the token on which cancellation was requested. The preferred way to do this is to use the ThrowIfCancellationRequested method. A task that is canceled in this way transitions to the Canceled state, which the calling code can use to verify that the task responded to its cancellation request

There is also a code example demonstrating how to use ThrowIfCancellationRequested appropriately.

Another quote reading further into this Microsoft source:

When a task instance observes an OperationCanceledException thrown by user code ... the task interprets this as acknowledging cancellation and transitions to the Canceled state.

...

Also note that the presence of other exceptions will also cause the Task to transition to the Faulted state ...

  • I suspect (and this is just me attempting to reconcile the two sources) that this is what the recommendation you quoted is referring to--it's a recommendation to avoid disrupting the cancellation mechanism's ability to accurately indicate the task's state as canceled or faulted
egnomerator
  • 985
  • 8
  • 15