Cancelling a BackgroundWorker, how to prevent race when it has already finished
.net, backgroundworker, multithreading
Solution
Your `DoWork` event handler shoud periodically check `BackgroundWorker.CancellationPending`, and set `DoWorkEventArgs.Cancel` to true before returning if it was cancelled.
Your `RunWorkerCompleted` event handler should check the `RunWorkerCompletedEventArgs.Cancelled` property to determine if the `DoWork` event handler cancelled (set `DoWorkEventArgs.Cancel` to true).
In the event of a race condition, it may happen that the user requested cancellation (`BackgroundWorker.CancellationPending` is true) but the worker didn't see it (`RunWorkerCompletedEventArgs.Cancelled` is false). You can test these two properties to determine that this has occurred, and do whatever you choose (either treat it as successful completion - because the worker did actually finish successfully, or as a cancellation - because the user has cancelled and doesn't care any more).
I don't see any situation where there is any ambiguity about what happened.
EDIT
In response to the comment - if there are several classes that need to detect `CancellationPending`, there's no really no alternative to passing to these classes a reference to a type such as `BackgroundWorker` that allows them to retrieve this information. You can abstract this into an interface, which is what I generally do as described in this response to a question about BackgroundWorkers. But you still need to pass a reference to a type that implements this interface to your worker classes.
If you want your worker classes to be able to set `DoWorkEventArgs.Cancel` you will need to either pass a reference to this around, or adopt a different convention (e.g. a boolean return value or custom exception) that allows your worker classes to indicate that cancellation has occurred.
Problem
I'm using a BackgroundWorker to perform a long computation (only one such computation at a time). The user has the possibility of cancelling the worker (calling worker.CancelAsync). In the worker.DoWork method I periodically check for the cancel pending flag and then return from the method. Then the Completed event is raised from the worker and I can check that the worker was cancelled. Furthermore, and that is the important thing, I do some extra cleanup when a cancel is detected. I am sure there could be problem if the user cancels the worker and it's already returned from the DoWork method. In that case I would really like to know that the worker was cancelled so I could cleanup... Is there a better way to handle the cancel procedure, with cleanup, of a worker ?