C# Adding code to FormClosing event disables X on MdiParent
I have two background threads and a thread to handle minimization running on my WinForms Application. When the program closes, I use this method.
private void MyForm_Closing(object sender, FormClosingEventArgs e)
{
if(labelsUpdaterThread.IsAlive == true)
labelsUpdaterThread.Abort();
if(printNotifyThread.IsAlive == true)
printNotifyThread.Abort();
if(minimizeThread.IsAlive == true)
minimizeThread.Abort();
}
labelsUpdaterThread and printNotifyThread run all the time. MinimizeThread, as you might guess, only runs when the parent form is minimized. Here's my problem:
When the thread.abort methods are called in my abo开发者_开发百科ve method, the "X" on the top right of my MdiParent form doesn't do anything. Clicking it has no effect.
When the thread.abort methods are NOT called in my above method, closing the MdiParent will sometimes throw exceptions because the Threads are still trying to access resources on the MdiParent that are no longer available, even though they are background threads!
I'm unsure as to why this is happening, doesn't make much sense to me. Thanks in advance for any help!
I agree with Paul Alexander's answer in that you should never call Thread.Abort
, it's a horrible way to handle synchronization.
Additionally, you have a horrible separation of concerns here. The threads should not access resources in the form directly. There should be some sort of abstraction/shared-state in between, which is modified and read by both sides (the thread and the form, so make sure to make the instance thread-safe).
That said, if you couldn't make those changes then in the Close method, call the Thread.Abort
methods in another thread with a try/catch statement around each. Log the error(s) somewhere at least.
In performing the calls to Thread.Abort
on another thread, you don't block the UI thread, as the calls to Thread.Abort
are not guaranteed to be instantaneous, and blocking the UI thread will cause the X to be greyed out while the UI thread cannot process Windows Messages (it also helps to guide you to a better separation of concerns).
However, you should move to abstract out the resources that are shared between the form and the thread, as well as provide a proper cancellation mechanism.
If you abstract out the resources to a class that shares the state, then your form doesn't have to do anything on close, the thread call stacks have a reference to the object that has the state and you can then call abort on those threads without worrying about the form and the threads sharing anything.
From there, you can then introduce a proper cooperative cancellation mechanism (cooperative cancellation, which Task supports in .NET 4.0, if that's available to you).
The Abort calls are probably throwing exceptions. Make sure that the pointers are valid and threads are still valid (not disposed) before calling abort.
And, in visual studio, open Debug\Exceptions... and set a check in the 'thrown' column for all exceptions so you see when something goes wrong.
First, Delete the calls to .Abort() and never use them again. Threads should never be terminated by calling Abort. It basically crashes your thread and doesn't give it a chance to release any resources properly or free any system handles. Instead create a ManualResetEvent
and check that in your threads. When the event is set, they should terminate.
Thread1
while( ! _stopEvent.WaitOne(0) )
{
...do my thready work
}
Then in closing
private void MyForm_Closing(object sender, FormClosingEventArgs e)
{
_stopEvent.Set();
labelsUpdaterThread.Join();
...
}
If you don't care if the threads terminate properly on application exist, just set IsBackground = true
and they'll be terminated automatically when the application exits.
精彩评论