Answered: Threading, Invoking and FormClosing

UncleRonin

Well-known member
Joined
Feb 28, 2006
Messages
230
Location
South Africa
Programming Experience
5-10
Okay, this is the situation...

I have an application that has a GUI and a couple of threads that run in the background. Every now and then one of the threads has to update a GUI control so I call Invoke and update it. When the application closes the threads must finish their execution cycle so I force the application to wait before closing (I have Closing and a Processing variables so that while the application is not Closing the threads will continue to execute and while Processing the application will not close).

The interesting thing now is that if the application is closing and a thread is busy executing and calls Invoke, the application hangs. If I call BeginInvoke everything is 'okay' and the thread stops executing and the application closes happily. My question is this: what exactly happens with Invoke when an application is closing? Obviously it's blocking somewhere inside the whole Invoke business and not returning so the thread ends up in stasis... but why??

What happens to the application event queue when it starts closing? What happens inside Invoke? Why is the weather so terrible today!?! :confused:
 
Er.. When you are FormClosing and youre waiting for the threads to finish.. are you repeatedly checking the processing variable? i.e. are you putting the Invoker thread into a loop from whence it cannot escape to service other requests, such as window draws? We need to see code, really.. I get the feeling you wouldnt have any of these problems if you used a BackgroundWorker.. It can raise an event when it's finished. If you want to close before it's done, just set a flag that you want to close the app, have the workercompleted event check the flag and exit if necessary
 
Okay, I don't have my actual source with me but this is pretty much it in pseudocode:

VB.NET:
FormClosing_Handler()
     Me.Closing = True
     While Not Me.Processing
          Thread.Sleep(50)
     End While
End Handler

VB.NET:
Thread_RunMethod
     Me.Processing += 1
     While Not Me.Closing
          'Do Stuff
          Me.Invoke(Delegate)
          Thread.Sleep(50)
     End While
     Me.Processing -= 1
End Handler

Hmm... what exactly am I doing wrong with this? I've used this style a lot in the past and it's always worked except I've never included Invoke before. Can you explain to me exactly what's going on? I work with a pogress bar in FormClosing as well and it updates as expected and displays fine... :confused:

Oh, and I instantiate threads from within the FormLoad event with the addresses of my threading functions so no custom classes are involved.
 
I don't think there's enough code to properly ascertain what is going on but basically, youre putting threads into loops from which they cannot escape until some variables have been set

Don't do this! Certainly never, ever sleep a UI thread, even for 50ms and never use a tight loop on Sleep() to wait

Instead, have your workers raise an event when they are finished, and in the event handler, check to see if a quit was requested during processing. If it was, quit the app.

My big problem with your code there is that Form_Claosing will be called by the UI thread, but Me.Invoke(delegate) will also have the ui thread execute the delegated method. One thread cannot do both. If Delegate is the worker task, then youre getting the UI thread to do the work, so it is not free to handle closing events

Go back to what I saiod earlier
Use a BackgroundWorker to do your work, have a whole List(Of..) them if youre doing multiple things. When closing, looop the list and see if worker.IsBusy and if so set _quitRequested = True
In the Worker finished event, if all other workers are Not IsBusy AndAlso _quitRequested Then Exit(0)
 
I've never used BackgroundWorkers before actually. If the main application closes as the result of an unknown exception what happens to the BackgroundWorker? Will it be destroyed along with the main app or not?

Also my threads must loop and continue operating throughout the life span of the application so to replicate this with Workers will I have to keep calling the Workers work event from within its completed event handler? So basically it will work->complete->work->complete->... until it's requested to quit?

*nods* as far as my current code goes I'm not super keen on relying on the variables to be set either but except in the case of some really unknown exception it will always work. I don't really like while loops either to tell the truth and I would much rather use an alternative but when it comes to threading I'm lank used to them... kinda weird, eh?

I must admit I don't really like the idea of FormClosing specifying that quit is requested and then having its handler cancel the Closing event and disable the user interface while the Workers do their thing. Also all the additional events resulting from the worker calling itself over and over again aren't that appealing. I like event driven stuff but if it's gonna put itself into a loop then why not just use a loop? :cool: Logically it's the same thing... and except for the UI sleeping the user will experience the same thing and since it is the application closing there is no real need for the UI thread to do anything... except handle the Invoke that is.

I know my code isn't ideal *cough* but Workers don't strike me as all that much better in this case. They're probably way safer though.

The delegates my threads call update the UI... so as far as I can tell the main application thread does its thing and then the closing event triggers. The closing event then gets handled and loops. One of my threads then calls Invoke which then sits in the queue and waits to execute (which can only happen after the previous events a la FormClosing have finished executing). So until the FormClosing event is handled Invoke will sit there and the Thread will block. At the same time the FormClosing is looping and waiting for the thread and it will block. That's pretty much the mess I've made isn't it?

Using BackgroundWorkers will this problem be removed? Like, will i still be able to update the UI from these Workers while the application runs or will I still have to call Invoke from the Workers? If that's the case then there is a problem :(

Out of interest, if I instead use BeginInvoke rather than Invoke, what happens to the Invoke event that was placed in the queue? Will it vanish into the mists or sit in memory? Probably just vanish I suppose when the application is disposed (if it is in the app event queue?) or just be ignored by the OS since it doesn't have a target?? Win32 > Me!
 
That's pretty much the mess I've made isn't it?
Yes :) You can only update UI from a different thread by invoking on UI thread, if UI thread is locked by a loop (which you should not do) then UI update is not possible.

It doesn't matter if you're using Thread or BackgroundWorker (which runs on a Threadpool background thread), you have to manage synchronization either way, or at least analyze the situation and make a stand.
If you don't care whether the worker thread is done and it's safe to abort the work instantly then using a BGW or a Thread (IsBackground=True) will both end automatically when main thread ends, a foreground Thread can be called Abort explicitly also.
The safe way is to use FormClosing to set a CancellationPending Boolean flag in order for the worker to check and be able to stop working, this event can also be used to set another boolean like IsClosing that the worker thread can check after it's done and call for Close. Here is a very simplified code example of what I mean:
VB.NET:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim t As New Threading.Thread(AddressOf DoWork)
    t.Start()
End Sub

Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    If Me.IsBusy Then
        Me.IsClosing = True
        Me.CancellationPending = True
        e.Cancel = True '< don't allow form to close until worker have stopped cleanly
    End If
End Sub

Private IsClosing As Boolean
Private CancellationPending As Boolean
Private IsBusy As Boolean

Private Sub DoWork()
    Me.IsBusy = True
    While Not Me.CancellationPending
        'work...
        Threading.Thread.Sleep(5000)
    End While
    Me.IsBusy = False

    If Me.IsClosing Then
        Me.Invoke(New MethodInvoker(AddressOf Me.Close))
    End If
End Sub
 
I've never used BackgroundWorkers before actually. If the main application closes as the result of an unknown exception what happens to the BackgroundWorker? Will it be destroyed along with the main app or not?
Whole lot is killed

Also my threads must loop and continue operating throughout the life span of the application
No, your threads must work while there is work to be done. This is subtly different to what you said.

so to replicate this with Workers will I have to keep calling the Workers work event from within its completed event handler? So basically it will work->complete->work->complete->... until it's requested to quit?

Well, generally you have a queue of work items that a particular worker must perform. Work is done in a loop such that if there are items in the lsit, a worker will take one out, and work on it. A producer process loads the list with work and starts the worker if it is not running

You dont state whether you have several workers working on one list of tasks or whether you have one worker per task type?


*nods* as far as my current code goes I'm not super keen on relying on the variables to be set either but except in the case of some really unknown exception it will always work.
A WorkItem should be able to handle all the exceptions (best plan) so that the worker doesnt see any exception, it just stops work on that item. Given that all exceptions originate from the base Exception class, then catching it will mean that there can be no "really unknown exceptions"



I don't really like while loops either to tell the truth and I would much rather use an alternative but when it comes to threading I'm lank used to them... kinda weird, eh?
Never put your UI threads into loops. Avoid putting one thread into a loop that it relies on another thread to escape from.. Those situations have deadlock written all ovr them

I must admit I don't really like the idea of FormClosing specifying that quit is requested and then having its handler cancel the Closing event and disable the user interface while the Workers do their thing.
Huh? Why not? It makes the most sense!

User clicks close. Message appears: "The app cant be closed right now, because some workers are finishing up. The app will close when all workers are done. No new work will be started" [OK]

I never said anything about disabling a user interface; youre doing that by putting the UI thread into a loop ;)


Also all the additional events resulting from the worker calling itself over and over again aren't that appealing. I like event driven stuff but if it's gonna put itself into a loop then why not just use a loop?
[/quote]
Because youre not chaining events together to cause a loop. Think about cooking a meal in a microwave. You dont get up from the sofa every minute and look a tthe microwave to see if it has finished, you wait till you hear the beep - youre responding to an event. Same in the form, you dont check every 50 millisenconds that your workers have finished.. You either keep track of the number of workers that are working and when the count falls to 0, do something, or you count all the active workers every time one of them finishes and if there are no more actives, then you carry out the waiting actions

I know my code isn't ideal *cough* but Workers don't strike me as all that much better in this case.
Theyre a different skin on the same engine. The thing with a BGW is the invoking is handled for you and presented in a fashion that youre already familiar with (event model) which means you dont end up implementing a broken solution..




Using BackgroundWorkers will this problem be removed? Like, will i still be able to update the UI from these Workers while the application runs or will I still have to call Invoke from the Workers?
The BGW handles the invokes itself, and MS got it right already, so there's no need to reinvent their wheel


Out of interest, if I instead use BeginInvoke rather than Invoke
You want to BeginInvoke 200 times a second?
 
Hmm... JohnH you pointed out something I overlooked altogether - check if the form is closing before invoking any UI events! *slaps forehead* that way there is no chance of a deadlock and the enchanted forest will once again be at peace... *bliss*

Just for interest's sake... My one thread continuously checks my database's availability and displays it's status in the status bar. Another thread continously checks the database for new data and processes it and every time it reads in new data or stores new data it updates the status bar to show when the last successful read or store took place - this has to happen very frequently and continously because in production the data comes in quickly and needs to be processed even faster (it comes from another application altogether). It has to try and appear as real time as possible and it helps me with trouble shooting too. Each thread does it's own thing and has all of it's own objects except for the shared UI.

As for unknown exceptions I mean things like totally bizarre errors that occur once in a lifetime that you would really never expect. Like stuff resulting from a corrupt OS or bad memory or something. Try..Catch-ing a thread run method handles this though.

I really don't think there is all that much of an issue with sleeping the UI thread in this case. I only sleep it when the application is closing and I use a busy cursor and progress bar to indicate the closing process. Random note but here's a question for everyone: What is the best way to update a progress bar - estimated percentages, statistical percentages or just +1 for every line of code executed? Or just make it keep going so it looks good and makes the user feel comfortable? A conundrum! Back to the actual topic, if the UI thread sleeps, the progress bar updates happily and the cursor shows its 'thinking' and the user can't do anything to the application except watch contentedly as it closes gracefully... *happy sigh* I suppose I could make it prettier but it shows what's going on and prevents the user from fidgeting with things. I must point out that there are NO UI actions that should be allowed while the application is closing so the UI would have to be disabled to stop the restless types ;)

Cjard you mentioned havng a process which will give the BGWs their work to do. This pretty much means that if DBThread isn't busy, make it do stuff and if the DataThread isn't busy make it do stuff, yes? This producer must then be a thread itself that runs continuously throughout the application's life span (BGW that continuously checks the others' status and whether the application is closing or not so if it is it'll just stop and so will the delegation of work OR it's just a thread with a while true loop and background = True)?

So the end result is that if I wanted to be spiffy I would create a bunch of background workers and have a producer that makes them do stuff. These little worker fellows would then continue to work until the form closes and the producer stops delegating items. So they would then all die. Codewise this means having a whole bunch of event handlers as opposed to a few methods passed to Thread constructors but in terms of code length it's not really all that much extra. Disposal of objects due to unknown exceptions wouldn't be an issue since when the Form dies the worker dudes would die with it. And that as they say is that! Lemme know if I've got it wrong somewhere!!

For this particular app I'll just case and leave the synchro as it is but in future I'll definitely make a plan and use BGWs as MS intended *nods*
 
if the UI thread sleeps
You're missing the point. Don't sleep UI thread! You're stopping the message pump, and the only way out of that is to DoEvents, which just cancels the effect and reason for your Sleep calls in the first place.
I really don't think there is all that much of an issue with sleeping the UI thread in this case
Yes, there is, as much as doing it anywhere else during app lifetime. You will leave the window frozen and OS will consider that app has stopped responding to window messages and crashed.
 
Just realised I haven't closed this thread and whatnot...

I found a bug where my synchronised logging function was causing a deadlock (still don't see why it would when they are sequential) and caused all this nonsense in the first place! Fixed that and everything lived happily ever after.

Taking into account everything you guys said, at the end of it all I implemented a timer which will get enabled when the form closes. The timer then triggers every 100ms and increments a counter (progress bar) so long as the background threads are still active. If for some reason the counter (progress bar) reaches a value of 90 all active threads are aborted and the application closes (The only reason it would take so long to close is if a background query was busy timing out because the db wasn't available). Ever since that was done everything has worked 100% and the UI works just as it should :)

Super glad you mentioned that stuff about the OS thinking the program wasn't responding otherwise I coulda had some serious issues!

Anyways, I hereby close this thread and thank you all for the help :)
 
UncleRonin said:
close this thread
We're not closing threads here unless they for some reason need to be stopped, but keep them open for later, be it further inquiries or more knowledgable input :) I've opened this thread again. Telling that you're happy with the response or setting a "Answered" thread tag is a different matter than closing the thread for posting.
 
My bad! :eek: Wasn't sure what the policy on reviving old threads and that was so have been closing them just in case :eek:

EDIT: Dunno how to tag it as answered so changed the thread topic to say answered... that ruler is coming to slap me on the hand again isn't it?? :confused:
 
Not sure exactly how some do that, perhaps you can do it by editing first post in thread? I usually stop giving feedback when questions appears adequatly answered and OP says "thanks" :)
 
Back
Top