Canceling delegates on form close (C#) - c#

A quick question about concurrency in C#.
I have in my code the following method to update a textbox:
public void diag(string text)
{
if (InvokeRequired)
{
Invoke(new Action<string>(diag), text);
}
else
{
tbDiag.Text += text;
}
}
This method gets called in any number of methods in my program to report stuff. Among them, it gets called in the ReportProgress of a BackgroundWorker: this happens periodically every few tens of milliseconds.
If I close the form (and the application) without stopping the BackgroundWorker, i get a System.InvalidOperationException because the TextBox has been disposed.
Is there a way to prevent this from the handler for the FormClosing event?
Either cancelling the running delegate or waiting for its completion would be fine, just as long as I can get to prevent that exception being thrown.
What I would like to avoid is having to keep a direct reference to the delegate.
I apologize if this was answered already somewhere else. I have been googling this to no avail.
Thank you.

Related

Why does a Control's BeginInvoke() target delegate never occur after Dispose() is called on the Control?

I'm trying to fill a knowledge gap. I have a control that calls BeginInvoke(delegate), then afterward the control immediately disposes itself.
The delegate never seems to occur. It would seem to be a result of the Control.Dispose().
My confusion lies in the fact that (I thought) BeginInvoke places the delegate onto the Windows Message Queue to be processed later on the UI thread. Why would disposing the control have anything to do with this delegate no longer firing? It was already placed on the queue before disposing.
Also, if it has something to do with the Windows Handle, why do I not get an Exception instead of a quiet ignore of the delegate?
below is a simple example of what I mean:
class myControl : UserControl
{
public myControl()
: base()
{ }
public void DoBeginInvoke()
{
this.BeginInvoke(new MethodInvoker(
() => { Console.WriteLine("!!TESTING 123!!"); }
));
// silently prevents the delegate from occuring..
this.Dispose();
}
}
Thanks in advance for your explanation. Apologies for the simplistic question.
As far as I know, BeginInvoke is implemented as a PostMessage, and Dispose will call DestroyWindow on Window Handle
Check this answer and the linked MSDN page about DestroyWindow: it say it
flushes the thread message queue.
So it means your BeginInvoke will be flushed as well

Accessing the GUI from a background Thread

I've made a sample program which generates every second 2000 integers on a Background thread, and when it finishes it fires an event which draws graph on the GUI from the random generated data (I have a sleep inside my thread to simulate a real measurement).
private void SetChart(System.Windows.Forms.DataVisualization.Charting.Series series)
{
if (InvokeRequired)
{
SetChartCallback d = new SetChartCallback(SetChart);
this.Invoke(d, new object[] { series });
}
else
{
chart1.Series[0] = series;
chart1.Series[0].Name = "Generated Data";
}
}
I found this approach on the MSDN site. It's working fine, the only problem is, when I close the application. Sometimes an error meassage shows up :
Cannot access a disposed object.
Object name: 'Form1'.
When I close the program it disposes all the elements, how can I prevent this error not to happen?
You've closed the form, but the thread is still running, so when it completes It tries to invoke a method on the disposed object. Your form.
You can wait for the thread to complete.
Or you can signal it somehow to stop messing about creating integers you don't need anymore and quit it's loop right now.
Don't be tempted to just kill it. Very bad habit, you don't want get into that.
The proper approach, ugly as it may seem, is probably to catch the exception and swallow it. It's probably not reasonable for the form's Dispose to block until the background thread exits (a situation which could easily cause deadlock); nor does the Framework provide any method which says try to Invoke or BeginInvoke this method on a control or form, but simply do nothing if it's been disposed. Thus, your best bet is probably to write TryInvoke and TryBeginInvoke methods which will do that by catching any exception that results if the form has been disposed. You might use an IsDisposed check within such a method, but you should realize that because of some Framework quirks, there are some race conditions which cannot be resolved nicely.
A solution may be to check IsDisposed. Something like this:
private void SetChart(System.Windows.Forms.DataVisualization.Charting.Series series)
{
if (IsDisposed)
return;
// ...
}

Winform: Multiple Threads Updating UI at Same Time

I inherited some code that has two non-UI threads that update various WinForm controls.
The code is using InvokeRequired and Invoke to update the UI; however, I still once in a while get the error: Cross-thread operation not valid: Control 'lvReports' accessed on a thread other than it was created on.
I suspect I am dealing with a race condition and that I need to introduce a lock into the method below, but that said, I can find dozens of examples on how to update UI from a non-UI thread safely but no examples or discussion on how to deal with two threads updating the same controls in a race scenario.
So my question is: how do I rewrite the code below to handle updating the UI properly given a race condition and that I need to update UI from non-UI threads?
// two separate theads call this method in a instance of a WinForm
private void LoadReports()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(this.LoadReports));
}
else
{
// some code removed to keep exampe simple...
SetCtlVisible(lvReports, true);
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate { lvReports.Refresh(); });
}
else
{
lvReports.Refresh();
}
}
}
delegate void SetVisibleCallback(Control ctl, bool visible);
private void SetCtlVisible(Control ctl, bool visible)
{
if (ctl.InvokeRequired)
{
SetVisibleCallback d = new SetVisibleCallback(SetCtlVisible);
ctl.Invoke(d, new object[] { ctl, visible });
}
else
{
ctl.Visible = visible;
}
}
Here are some thoughts:
Does this.InvokeRequired differ from ctl.InvokeRequired at any time?
Is the second InvokeRequired test needed given the first?
Is the implementation of SetCtlVisible needed if I keep the first InvokeRequired?
Should I delete the first InvokeRequired and keep all the code in the else clause?
Is lock needed around the else clause?
Using InvokeRequired like this is an anti-pattern. You know that this method is getting called from a thread, InvokeRequired should always be true.
Now you can use it to troubleshoot your problem. If it is false then there's something seriously wrong. Throw an exception, the debugger will stop and let you find out why it isn't working properly. And always call Invoke(), invoke to a little helper method that does the rest of LoadReports().
Also note that you are using it wrong in the rest of your code. You know that the remainder of LoadReports() runs on the UI thread, you used Invoke(). No point in testing it again, including inside SetCtlVisible().
The typical reason for getting the bomb is because the thread is running LoadReports() too soon, before the form's window is created. You need to interlock that. The form's Load event is the signal.

Object Disposed exception and multi thread application

I have an application that start System.Threading.Timer, then this timer every 5 seconds read some information from a linked database and update GUI on main form of application;
Since the System.Threading.Timer create another thread for the Tick event, i need to use Object.Invoke for updating User Interface on the main Form of application with code like this :
this.Invoke((MethodInvoker)delegate()
{
label1.Text = "Example";
});
The app work very well, but sometimes when the user close the main form and then close the application, if the second thread on timer_tick event is updating the user interface on main thread the user get an ObjectDisposedException.
How can i do for stop and close the threading timer before closing the main form and avoiding then Object disposed exception ?
This is a bit of a tricky proposition as you must ensure the following on a given Close event
The timer is stopped. This is fairly straight forward
The control being updated isn't disposed when the delegate is run. Again straight forward.
The code currently running off of a timer tick has completed. This is harder but doable
There are no pending Invoke methods. This is quite a bit harder to accomplish
I've run into this problem before and I've found that preventing this problem is very problematic and involves a lot of messy, hard to maintain code. It's much easier to instead catch the exceptions that can arise from this situation. Typically I do so by wrapping the Invoke method as follows
static void Invoke(ISynchronizedInvoke invoke, MethodInvoker del) {
try {
invoke.Invoke(del,null);
} catch ( ObjectDisposedException ) {
// Ignore. Control is disposed cannot update the UI.
}
}
There is nothing inherently wrong with ignoring this exception if you're comfortable with the consequences. That is if your comfortable with the UI not updating after it's already been disposed. I certainly am :)
The above doesn't take care of issue #2 though and it still needs to be done manually in your delegate. When working with WinForms I often use the following overload to remove that manual check as well.
static void InvokeControlUpdate(Control control, MethodInvoker del) {
MethodInvoker wrapper = () => {
if ( !control.IsDisposed ) {
del();
}
};
try {
control.Invoke(wrapper,null);
} catch ( ObjectDisposedException ) {
// Ignore. Control is disposed cannot update the UI.
}
}
Note
As Hans noted ObjectDisposedException is not the only exception that can be raised from the Invoke method. There are several others, including at least InvalidOperationException that you need to consider handling.
System.Timers.Timer is a horrible class. There is no good way to stop it reliably, there is always a race and you can't avoid it. The problem is that its Elapsed event gets raised from a threadpool thread. You cannot predict when that thread actually starts running. When you call the Stop() method, that thread may well have already been added to the thread pool but didn't get around to running yet. It is subject to both the Windows thread scheduler and the threadpool scheduler.
You can't even reliably solve it by arbitrarily delaying the closing of the window. The threadpool scheduler can delay the running of a thread by up to 125 seconds in the most extreme cases. You'll reduce the likelihood of an exception by delaying the close by a couple of seconds, it won't be zero. Delaying the close for 2 minutes isn't realistic.
Just don't use it. Either use System.Threading.Timer and make it a one-shot timer that you restart in the event handler. Or use a System.Windows.Forms.Timer, it is synchronous.
A WF Timer should be your choice here because you use Control.Invoke(). The delegate target won't start running until your UI thread goes idle. The exact same behavior you'll get from a WF timer.
Create two booleans called 'StopTimer' and 'TimerStopped'. Set the timer's AutoReset property to false. Then format the Elapsed method to the following:
TimerStopped = false;
Invoke((MethodInvoker)delegate {
// Work to do here.
});
if (!StopTimer)
timer.Start();
else
TimerStopped = true;
This way you are preventing a race condition, checking if the timer should continue and reporting when the method has reached its end.
Now format your FormClosing event as follows:
if (!TimerStopped)
{
StopTimer = true;
Thread waiter = new Thread(new ThreadStart(delegate {
while (!TimerStopped) { }
Invoke((MethodInvoker)delegate { Close(); });
}));
waiter.Start();
e.Cancel = true;
}
else
timer.Dispose();
If the timer hasn't stopped yet, a thread is launched to wait until it has done so and then try to close the form again.

Threading Method Question

I'm using the following method to show a modeless Message Box.
public void ShowMessageBox(string Message)
{
var thread = new Thread(
() =>
{
MessageBox.Show(Message);
});
thread.Start();
}
The "() => {...}" is something I've never seen before. What is the name for this code pattern?
Also, thread.Start starts the thread, and it automatically closes once the "()=>{...}" method completes (when the Message Box is OK'ed), right? If so, can you please point me to some official documentation saying that the thread closes automatically?
Thanks!
It's the lambda operator, and read as "goes to". MSDN has a good intro: Lambda Expressions (C# Programming Guide)
One concern with your example is that you're spinning up a new thread to update the UI, the UI is intrinsically single-threaded, so background updates are generally the wrong thing to do (unless you're manually/explicitly checking InvokeRequired and calling Invoke() as needed.
Regarding the UI threading...
In WinForms every Form or Control is created on a particular thread (the "UI Thread"), and you can think of that thread as owning that control (not exactly correct, but a good way to conceptualize it). Updating the UI from that thread is safe, updating the UI from another thread runs the risk of collisions and corruption and all the usual risks of parallel/async programming.
...So... how do you safely update the UI from a background thread without blocking the UI? In short--you can't--the best you can do is block it for the bare minimum required to update the UI. This is where InvokeRequired and Invoke() come in...
Here's a sample: you should be able to drop this into the code-behind of a new form with a button and textbox.
To use:
Try commenting out either the call to SetTextAsyncSafe() or SetTextAsyncSafe() -- running both could confuse you since they won't necessarily execute in the order they're called (they're running async, remember?).
Then set a breakpoint on SetText(). You should see the "safe" call will actually call the method twice--the first call will detect InvokeRequired and will call the method a 2nd time for the correct thread by Invoke()'ing to it.
You should see an Exception thrown when SetTextAsyncUnsafe() actually gets to the textBox1.Text = value; statements. The exception will be an InvalidOperationException with a message stating "Cross-thread operation not valid" -- you can google this term for more details.
The code:
private void button1_Click(object sender, EventArgs e)
{
SetTextAsyncSafe("This update was made from the UI Thread by using Invoke()");
SetTextAsyncUnsafe("This update was made directly from the background thread and can cause problems");
}
private void SetTextAsyncUnsafe(string value)
{
new Thread(() => SetText(value, false)).Start();
}
private void SetTextAsyncSafe(string value)
{
new Thread(() => SetText(value, true)).Start();
}
private void SetText(string value, bool checkInvokeRequired)
{
if (checkInvokeRequired)
{
if (InvokeRequired)
{
Invoke(new Action(() => SetText(value, checkInvokeRequired)));
return; // early exit
}
}
textBox1.Text = value;
}
That is a Lambda. In this case, you're using it to create a new anonymous method that will be run when the new Thread is started.
It's the (near) equivalent of:
public void ShowMessageBox(string Message)
{
var thread = new Thread(ShowBox);
thread.Start(Message);
}
public void ShowBox(object message)
{
MessageBox.Show(message.ToString());
}
This is called a Lambda Expression. You can read more here.
Lambda expression, C# version 3 feature.
Don't use this code. A message box needs a parent window, something it can make sure to be on top of. It can normally find a parent by itself by iterating the windows that were created on the same thread. Not in this case though, there are no other windows, it has to pick the desktop window as the parent.
That will go wrong badly when the user is working in an app window or switches focus to another app, the message box disappears behind the foreground window. There is no obvious way for the user to tell that it is there, she'll just loses sight of it. It could be hours, if not days, before she finds it back. That thread is meanwhile consuming resources badly, you would probably never consider it if you knew that this message box requires a megabyte of memory. In extreme cases, you'll crash the program with OOM.
The common alternative in Windows UI programming is a balloon tooltip provided by a NotifyIcon. Or your own form with the TopMost property set to True so it cannot easily get lost. Also allows you to control the position, important for "non-modal" notifications that should not get in the way. Set that form's ShowWithoutActivation property to true in the form constructor so it doesn't steal the focus.
Its a statement lambda.
Yes, thread is active as long as this anonymous method is running. Since after MessageBox.Show() there is no other statements, thread will exit, and this must be true... if you are in doubt add this before start:
thread.Name = "LALALA";
And then debug your app. When the message box apear, pause execution, go to Threads View and you will see LALALA running. Click OK and pause again, there should be no "LALALA"... =)

Categories