Sign up ×

The way to cancel a BackgroundWorker's operation is to call BackgroundWorker.CancelAsync():

private void cancelButton_Click(object sender, EventArgs e)

In a BackgroundWorker.DoWork event handler, we check BackgroundWorker.CancellationPending:

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    while (!backgroundWorker.CancellationPending) {

The above idea is all over the web, including on the MSDN page for BackgroundWorker.

Now, my question is this: How on earth is this thread-safe?

I've looked at the BackgroundWorker class in ILSpy — CancelAsync() simply sets cancellationPending to true without using a memory barrier, and CancellationPending simply returns cancellationPending without using a memory barrier.

According to this Jon Skeet page, the above is not thread-safe. But the documentation for BackgroundWorker.CancellationPending says, "This property is meant for use by the worker thread, which should periodically check CancellationPending and abort the background operation when it is set to true."

What's going on here? Is it thread-safe or not?

share|improve this question
Note that Jon Skeets example uses a static var, not a property. I think the property prevents caching of the value and that makes it completely thread-safe. –  Henk Holterman Aug 6 '11 at 12:53
Hmm, interesting. I didn't consider that a property might be different. I wonder whether that is documented anywhere… –  Tom Aug 6 '11 at 13:02
Then again, BackgroundWorker.CancelAsync() sets the field, not the property, to true, so I guess there could be a problem of writing only to the cache. –  Tom Aug 6 '11 at 13:05

4 Answers 4

up vote 8 down vote accepted

It is thread-safe.
The code

  while (!backgroundWorker.CancellationPending) 

is reading a property and the compiler knows it can't cache the result.

And since in the normal framework every Write is the same as VolatileWrite, the CancelAsync() method can just set a field.

share|improve this answer

It is because BackgroundWorker inherits from Component which inherits from MarshalByRefObject. An MBRO object may reside on another machine or in another process or appdomain. That works by having the object impersonated by a proxy that has all of the exact same properties and methods but whose implementations marshal the call across the wire.

One side effect of that is that the jitter cannot inline methods and properties, that would break the proxy. Which also prevents any optimizations from being made that stores the field value in a cpu register. Just like volatile does.

share|improve this answer
My understanding was that question asks specifically about backing field of the CancellationPending, not about the property itself (the property is a feature of the language and is just a method from CLR standpoint). So what prevents backing field from being cached by reading thread? –  Dmitry Aug 6 '11 at 17:20
@Dmitry - The fact that it is private (cancellationPending, lower case first letter). Client code has to use the property. –  Hans Passant Aug 6 '11 at 18:00
As a consultation do you think in this case the scenario I described in my answer will not happen even on a weak memory models? –  Jalal Aldeen Saa'd Aug 6 '11 at 18:08
@Dmitry - declaring it volatile makes no difference, it is already treated volatile. –  Hans Passant Aug 6 '11 at 21:41
@Dmitry - I explained that in my answer. Sorry that I couldn't explain it well enough, the subject of volatile is fairly impenetrable until you grok machine code and why CPU registers matter. –  Hans Passant Aug 6 '11 at 23:52

A good point and a very fair doubt. It sounds like not thread safe. I think MSDN also has the same doubt and that's why this is there under BackgroundWorker.CancelAsync Method.



I am not sure about BackgroundWorker class implementation. Looking at how it is using BackgroundWorker.WorkerSupportsCancellation property internally is important in this case.

share|improve this answer
I see, but my main worry is that backgroundWorker.CancellationPending, in backgroundWorker_DoWork, may never evaluate to true, even if backgroundWorker.CancelAsync(); has been called, because of caching. If so, RunWorkerCompleted event will never be raised anyway. –  Tom Aug 6 '11 at 12:52
This is not what OP asked. The problem you are describing is caused by the fact that the worker thread may end before it has the chance to set the cancelled flag in event's args. OP is asking whether it is safe to assume that a different thread can read the current value of the flag after it has been set. –  Groo Aug 6 '11 at 16:25

Question can be interpreted in two ways:

1) Is BackgroundWorker.CancellationPending implementation correct?

It is not correct because it may result in cancellation request being unnoticed. The implementation uses ordinary read from the backing field. If this backing field is updated by other threads then the update may be invisible to the reading code. This is what implementation looks like:

// non volatile variable:
private Boolean cancellationPending;

public Boolean CancellationPending {
    get {
        // ordinary read:
        return cancellationPending;

The correct implementation would try to read the most up to date value. This can be achieved by declaring backing field 'volatile', using memory barrier or lock. There are probably other options and some of them are better than the others but is up to the team that owns 'BackgroundWorker' class.

2) Is code that uses BackgroundWorker.CancellationPending correct?

while (!backgroundWorker.CancellationPending) {

This code is correct. The loop will spin until CancellationPending returns 'true'. Keeping in mind that C# properties is just a syntax sugar for CLR methods. At the IL level this is just another method that will look like "get_CancellationPending". Method return values are not cached by calling code (probably because figuring whether method has side effects is too hard).

share|improve this answer

Your Answer


