views:

272

answers:

3

My code launches a background thread. The background thread makes changes and wants the UI in the main thread to update. The code that launches the thread then waits looks something like:

                Thread fThread = new Thread(new ThreadStart(PerformSync));

                fThread.IsBackground = true;
                fThread.Start();

                fThread.Join();

                MessageBox.Show("Synchronization complete");

When the background wants to update the UI, it sets a StatusMessage and calls the code below:

    static StatusMessage _statusMessage;
    public delegate void AddStatusDelegate();
    private void AddStatus()
    {
        AddStatusDelegate methodForUIThread = delegate
        {
            _statusMessageList.Add(_statusMessage);
        };

        this.Dispatcher.BeginInvoke(methodForUIThread, System.Windows.Threading.DispatcherPriority.Send);
    }

_statusMessageList is an ObservableCollection that is the source for a ListBox.

The AddStatus method is called but the code on the main thread never executes - that is, _statusMessage is not added to _statusMessageList while the thread is executing. However, once it is complete (fThread.Join() returns), all the stacked up calls on the main thread are executed.

But, if I display a message box between the calls to fThread.Start() and fThread.Join(), then the status messages are updated properly.

What do I need to change so that the code in the main thread executes (UI updates) while waiting for the thread to terminate?

Thanks.

+4  A: 
fThread.IsBackground = true;
fThread.Start();

fThread.Join();

The call to Join() is blocking your main thread until the background thread finishes. So the UI thread can't do anything while the other thread is running. You need to not call Join(). If you need to do something on the main thread after the background thread finishes, find another way to do it.

EDIT: The reason it works if you display a message box between the call to Start and Join is because then the main thread is running the message loop for the message box, instead of being blocked at the Join call.

Daniel Plaisted
+3  A: 

fThread.Join causes your main thread to block until the background thread finishes. As long as the main thread is blocked, the UI cannot be updated.

What you need to do is something like this (untested, but you should get the idea):

void someUiEventThatCausesTheBackgroundProcessingToStart() {
    Thread fThread = new Thread(new ThreadStart(PerformSync));
    fThread.IsBackground = true;

    // disable some UI components here, so that the user cannot
    // start the thread a second time
    ...

    fThread.Start();

    // *don't* call Thread.Join here, so that your main thread does not block!
}

void PerformSync() {
    try {
        // do your stuff here
        ...
    } finally {
        Dispatcher.Invoke(new Action(ProcessingDone));
    }
}

void ProcessingDone() {
    // re-enable the UI components
    ...

    MessageBox.Show("Synchronization complete");
}

Of course, in WPF, disabling/enabling UI components is ideally done using some IsBusyProcessing dependency property which is bound with a trigger to the UI elements in question, but this is another story...

EDIT: As another option, you might want to check out the BackgroundWorker class, which contains ProgressChanged and RunWorkerCompleted events that automatically fire in the main thread.

Heinzi
A: 

The BackgroundWorker is the right tool for this job. It's no more complicated than is absolutely necessary, gives you all of the hooks that you need for progress reporting, and there's plenty of information available (such as this answer) on how to use it.

Robert Rossney