I'm quite new to this. There is a lot of information around, and I'm a bit confused. I would like to verify that I understood correctly and appreciate any help. I will number each of my questions in (i) so it would be easier to answer.
I have a BackgroundService with ExecuteAsync(CancellationToken cancellationToken) and StopAsync(CancellationToken cancellationToken).
I assume both methods will get the same token when the service is run. (1) Is this true?
If I understand correctly, I'm supposed to use the cancellationToken in ExecuteAsync to do something as long as cancellationToken is not marked for cancellation. ExecuteAsync has a while loop that runs iterations while cancellationToken is not cancelled. Each iteration awaits something to happen and may launch some async Tasks.
When I launch these tasks, I should give them a CancellationToken from a new CancellationTokenSource that is linked to cancellationToken so that (A) the task knows the stop both when the Service should be cancelled and when the task itself should be cancelled (but, in the latter case, without cancelling the Service itself); and (B) so that cancellationToken exhausted. (2) Are (A) and (B) correct?
Simply calling Cancel() on some CancellationTokenSource does not actually automatically cancels my Tasks or throws an exception. It is up to me to define this logic. (3) Is this true?
Which bring me to another question. Say that I discover insider some task that it was indeed marked for cancellation. What is the correct/expected behavior? Should I clean up the Task's resources and then use ThrowIfCancellationRequested() on the token that I have? (4) Is this true?
Lastly, how should I behave when the service itself is cancelled, i.e., when the main loop of ExecuteAsync discovers that its token is cancelled. (5) Should I immediately ThrowIfCancellationRequested? Should I first clean up resources and only then ThrowIfCancellationRequested? Or should I only call ThrowIfCancellationRequested as the last line of StopAsync?
Don't. You're mixing different levels of abstractions here.
Either use
BackgroundServicewithExecuteAsyncor useIHostedServicewithStartAsyncandStopAsync.You probably just need
ExecuteAsync. That's fine for most people.No. The cancellation tokens passed to
StartAsync,StopAsyncandExecuteAsyncare all different.Yes. The cancellation token passed to
ExecuteAsyncis cancelled when it's time for the background service to stop.Do you ever need to cancel just a single task? If so, then yes. If not, then just pass the
cancellationTokenon directly.Yes. Cancellation tokens must be observed. This is most commonly done by:
ThrowIfCancellationRequested.Better: always have your resources defined in
usingstatements. Then you can justThrowIfCancellationRequestedif you need to.Again, 99% of the time, properly handling cancellation is just a matter of passing the token on down to some other method that already supports cancellation.
Handle it the same exact way: if your methods are cancellable, just pass it on down. Keep resources in
usingblocks. If you need to check (most don't), you can periodically callThrowIfCancellationRequested.Specifically:
If your "awaits something to happen" is cancellable and your "launch some async Tasks" is cancellable, then just pass the cancellation token to both of these methods. No polling is necessary.
Don't use
StopAsyncif you're usingExecuteAsync.