Search code examples
c#backgroundworker

Thread safety in RunWorkerCompletedHandlers


I'm having trouble figuring out the thread safety implications of using RunWorkerCompletedHandlers. I found this elsewhere on Stack Overflow:

If a BackgroundWorker is created on the UI thread, DoWork will run on a thread pool thread and RunWorkerCompleted will run on the UI thread.

If a BackgroundWorker is created on a background thread (ie not the UI thread) DoWork will still run on a thread pool thread and RunWorkerCompleted will also run on a thread pool thread.

Given the following class:

class TestingClass
{
    private BackgroundWorker _bgWorker = new BackgroundWorker();
    private int _counter = 0;

    private readonly int _creatorID;

    public TestingClass()
    {
        _creatorID = Environment.CurrentManagedThreadId;

        _bgWorker.DoWork += DoAsyncWork;
        _bgWorker.RunWorkerCompleted += CompleteAsyncWork;
        _bgWorker.RunWorkerAsync();
    }

    public void IncrementCounter()
    {
        // We only allow the creator of this instance to call this function
        // because instances of this class will not be shared with other threads.
        Debug.Assert(_creatorID == Environment.CurrentManagedThreadId);

        ++_counter;
    }

    private void DoAsyncWork(object sender, DoWorkEventArgs e)
    {
        int i = 0;
        while (i < 100000)
            ++i;
    }

    private void CompleteAsyncWork(object sender, RunWorkerCompletedEventArgs e)
    {
        // Apparently _creatorID == Environment.CurrentManagedThreadId isn't guaranteed here!

        // Modification of member variable after the asynchronous part
        // has been completed.
        ++_counter;
    }
}

Since CompleteAsyncWork won't necessarily run on the thread that created the instance, I'm assuming that there's a chance the creating thread will be calling IncrementCounter while CompleteAsyncWork is being executed. Is it safe to modify member variables in a RunWorkerCompletedHandler in this case? If no, what would the right approach be?


Solution

  • If no, what would the right approach be?

    The right approach is to only create a BGW on the main GUI thread. They're not much use in any other way.