Windows Develop Bookmark and Share   
 index > Windows Forms General > How to wait for BackGroundWorker to complete
 

How to wait for BackGroundWorker to complete

I have a smart client application, and have some task that I wish to do in the background, so that the user can continue working with the UI. For this I am using System.ComponentModel.BackGroundWorker component that is available in the component toolbox (I guess it is available only in 2.0).

The BackGroundWorker component works fine for me, but the problem is, there is one thing on the UI that I cannot let the User to go ahead with until the BackGroundWorker finishes its task. For simplicity, assume that there is a button on the UI, which the user can freely click, but the processing code that I have written in the button click event handler should only be called after the BackGroundWorker thread finishes.

I have the following piece of code in the button click event handler

private void myButton_Click(object sender, EventArgs e)

{

this.WaitForBackgroundThreadToComplete();

// button click processing code here.

// ...

}

private void WaitForBackgroundThreadToComplete()

{

int maxThreads = 0;

int placeHolder = 0;

int availThreads = 0;

int timeOutSeconds = 100;

//Now wait until all threads from the Threadpool have returned

while (timeOutSeconds > -1)

{

//figure out what the max worker thread count it

System.Threading.ThreadPool.GetMaxThreads(out

maxThreads, out placeHolder);

System.Threading.ThreadPool.GetAvailableThreads(out availThreads,

out placeHolder);

if (availThreads == maxThreads) break;

// Sleep

System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(1000));

--timeOutSeconds;

}

if (timeOutSeconds < 0)

{

this.backgroundWorkerLookupLoader.CancelAsync();

throw new Exception(SourceConstants.LOOKUP_THREAD_TIMEOUT_ERROR);

}

}

Please note, that I have a timeout of 100 seconds, and under normal conditions, if I let the BackgroundWorker thread do its job it takes less than 10 seconds to complete....but if the user clicks the button before 10 seconds (i.e. when the background thread is stillactive), i eventuallycall the WaitForBackgroundThreadToComplete() and always get a timeout error there.

Sarvesh Jain  Thursday, July 05, 2007 3:44 AM

Hi Sarvesh Jain

The example below may meet your requirement. The main point in the example is that using an event for reporting background work completion event.

Code Snippet

public partial class MainForm : Form

{

// this event is used for reporting completion event from the BackgroundWorker

private event EventHandler BackgroundWorkFinished;

public MainForm()

{

InitializeComponent();

this.BackgroundWorkFinished += new EventHandler(MainForm_BackgroundWorkFinished);

}

private void backgroundWorkButton_Click(object sender, EventArgs e)

{

// run the background work

this.backgroundWorker.RunWorkerAsync();

}

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)

{

// simulate a long time background work

Thread.Sleep(5000);

}

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

if (this.BackgroundWorkFinished != null)

{

this.BackgroundWorkFinished(this, EventArgs.Empty);

}

}

private void MainForm_BackgroundWorkFinished(object sender, EventArgs e)

{

// after background work event

MessageBox.Show("Background work finished!");

}

}

Best Regards

Wei Zhou

You've diagnosed your problem correctly. You can't use a wait loop like you posted. The RunWorkerCompleted event handler won't run until your app goes idle and starts processing Windows messages again. And BackgroundWorker.IsBusy won't turn false until the RunWorkerCompleted event is processed. You've created a deadlock.

You can break the lock by calling Application.DoEvents() inside your wait loop. But that's mighty ugly and can cause a lot of problems. Just set a flag that indicates the user clicked the button. And start a timer. When RunWorkerCompleted fires, check the flag and execute the user's request. When the timer's Tick event fires, it is taking too long; cancel the request.

nobugz  Wednesday, July 11, 2007 10:54 AM
Hmya, you don't want to do it this way. Thread pool threads are also used by the CLR and the framework. Your problem is trivially solved with the BGW's IsBusy property and a Timer.
nobugz  Thursday, July 05, 2007 1:36 PM

I used the IsBusy property in the WaitForBackgroundThreadToComplete() method, but IsBusy always returns true even when the BGW has finished

void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

// some long task here....

// some long task continued....

// console message to indicate task completion

Console.WriteLine("Background doWork completes");

}

private void WaitForBackgroundThreadToComplete()

{

int i = 0;

while (backgroundWorker1.IsBusy)

{

Console.WriteLine(i++);

System.Threading.Thread.Sleep(1000);

}

}

Assume that the thread is still running (DoWork is in progress) and I call WaitForBackgroundThreadToComplete,

In my output, I see the message "Background doWork completes" indicating my backgroundWorker is done, but the IsBusy continues to be true and WaitForBackgroundThreadToComplete goes in an infinite loop.

Instead of this, if I set a boolean variable in the DoWork event it works satisfactorily, please refer to the code below

void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

// some long task here....

// some long task continued....

// set boolean variable to indicate work is completed

workCompleted = true;

}

private void WaitForBackgroundThreadToComplete()

{

int i = 0;

while (workCompleted == false)

{

Console.WriteLine(i++);

System.Threading.Thread.Sleep(1000);

}

}

However, my problem is I have to update the UI after DoWork event. To do that, I use the BackGroundWorker's RunWorkCompleted event. This event fires when DoWork is completed

void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

workCompleted = true;

}

void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

// some long task here....

// some long task continued....

}

private void WaitForBackgroundThreadToComplete()

{

int i = 0;

while (workCompleted == false)

{

Console.WriteLine(i++);

System.Threading.Thread.Sleep(1000);

}

}

if I do this, then WaitForBackGroundThreadToComplete method goes in an infinite loop. This is because I think, RunWorkerCompleted method runs on the main thread (and not the worker thread) and I am getting the thread to sleep in the WaitForBackgroundThreadToComplete method.

in short i need to do the following things

1. run a process in the background

2. once the background process completes, i want to update the ui

3. until that time, the user is free to use the UI, but at one particular point I need to wait for the background thread to finish only then i can allow the user to proceed.

Sarvesh Jain  Thursday, July 05, 2007 2:28 PM

Well, the IsBusy property does not work, nor does the timer control.

But I think I can throw light on where the problem is now. In the above code snippet I said that in the DoWork event handler of the BackgroundWorker component I am doing 'some long task'.

Let me elaborate on what I am doing there. I am calling a static method, which in turn calls a web service and returns me a custom collection. I did comment out that call on my machine and instead of that I just have a simple long loop (that simulates a long process). In this case the entire logic works fine.

Please let me know what could be wrong in the web services call, and how do I debug it and fix it?

Sarvesh Jain  Thursday, July 05, 2007 8:13 PM

Hi Sarvesh Jain

The example below may meet your requirement. The main point in the example is that using an event for reporting background work completion event.

Code Snippet

public partial class MainForm : Form

{

// this event is used for reporting completion event from the BackgroundWorker

private event EventHandler BackgroundWorkFinished;

public MainForm()

{

InitializeComponent();

this.BackgroundWorkFinished += new EventHandler(MainForm_BackgroundWorkFinished);

}

private void backgroundWorkButton_Click(object sender, EventArgs e)

{

// run the background work

this.backgroundWorker.RunWorkerAsync();

}

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)

{

// simulate a long time background work

Thread.Sleep(5000);

}

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

if (this.BackgroundWorkFinished != null)

{

this.BackgroundWorkFinished(this, EventArgs.Empty);

}

}

private void MainForm_BackgroundWorkFinished(object sender, EventArgs e)

{

// after background work event

MessageBox.Show("Background work finished!");

}

}

Best Regards

Wei Zhou

You've diagnosed your problem correctly. You can't use a wait loop like you posted. The RunWorkerCompleted event handler won't run until your app goes idle and starts processing Windows messages again. And BackgroundWorker.IsBusy won't turn false until the RunWorkerCompleted event is processed. You've created a deadlock.

You can break the lock by calling Application.DoEvents() inside your wait loop. But that's mighty ugly and can cause a lot of problems. Just set a flag that indicates the user clicked the button. And start a timer. When RunWorkerCompleted fires, check the flag and execute the user's request. When the timer's Tick event fires, it is taking too long; cancel the request.

nobugz  Wednesday, July 11, 2007 10:54 AM

You can use google to search for other answers

Custom Search

More Threads

• End Event Handling
• Graphics.MeasureString giving wrong size
• So many users so few computers
• Painting a Rounded Corner Rectangle with Open Top
• How to re-authenticate user?
• VB .net Combo Box property
• ComboBox SelectedIndex Crashes my program
• What is the best way to find out when DOM is finalized using Webbrowser control?
• Jumbling 30 picture boxes - challenge to you all
• MaxLength of a property in PropertyGrid to 25 characters