views:

471

answers:

4

Right, my application occassionally kicks off a background thread that does some stuff.

As its doing its stuff it also updates a progress bar on the main window. It does this by calling Invoke to get the main thread to update the interface.

When the user closes the application, I want to wait until all the background threads are finished before closing the form. In the form closing event I have something like

      while ( this._Queue.Count > 0 )
   Application.DoEvents ();

But this does not work!!

The background thread is getting stuck on the Invoke call. The main thread continues to loop around calling its DoEvents, which I thought would be all it needed to do to pick up and process its invokes. But it isnt doing this...

Why not!?!

A: 

I suggest putting the code in the "Closing" event of the form. Also, you may try adding a Thread.Sleep(100) after the Application.DoEvents().

Also, I'd create a ManualResetEvent called threadDone or similar that is set upon startup (pass true to constructor of event), gets reset before the thread is started and is set by the thread to indicated it is done working.

Then you could rewrite your loop as follows:

while (!threadDone.WaitOne(100, true))
{
    Application.DoEvents();
    Thread.Sleep(100);
}

This might help - but I haven't actually tried this right now. What I normally do is use the reset event to check whether the application or form can be closed. If the thread is not done, I show a message to the user.

Thorsten Dittmar
+1  A: 

The BackgroundWorker provides a convenient way to run background threads while allowing for progress to be reported easily using .NET events. You would hook up your ProgressBar UI update to the ProgressChanged event handler, and completion is reported back to you through RunWorkerCompleted. If you only have a single background thread, this is much simpler than rolling your own thread handling code. Here's the MSDN article:

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

If you're stuck with using regular threads, then I'd consider using Join to wait for the thread to complete. This keeps processing normal messages and saves you polling in a tight loop. There's more information here:

http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx

In addition, there are overloads that take a timeout parameter (either an Int32 for milliseconds or a TimeSpan object). If the thread has not completed after this time has elapsed, an exception is thrown. This helps catch threads which are stuck while you're trying to shutdown. More here:

http://msdn.microsoft.com/en-us/library/6b1kkss0.aspx

I hope this helps.

Dave R.
Join doesnt seem to work, it still gets into a deadlock with the main thread stuck on the Join, and the worker thread stuck on the Invoke.
Mongus Pong
A: 

The main thread should NOT wait for any other thread, rather the other thread should signal that its work has been completed.

In your case I would just use the Control.Invoke or the Control.BeginInvoke method from the other thread to update the UI.

Michael Damatov
I am, its not working..
Mongus Pong
The main thread should NOT be blocked by waiting for something, it should do nothing. In the same time the OTHER thread completes it work and notifies the main thread.
Michael Damatov
A: 

Right, I have sorted out the problem by taking it from a different angle. No more waiting for threads to finish...

I have just added a variable bool KillOnThreadFinish in the worker thread.

Then in the form closing of main form I check if the thread is running. If it is I set KillOnThreadFinish = true, I set e.cancel = true ( to prevent the form closing at that point ) and I set the MainForm.Enabled = false ( to prevent the user from closing the form again ).

Then when the thread finishes its work it checks KillOnThreadFinish and if True calls Application.Exit() and the app exits.

Seems to work beautifully!

(Thanks for trying guys. :-) )

Mongus Pong