Thread Control.Invoke

Thread Control.Invoke

There are two golden rules of threading in Windows Forms:

  • Don't touch any control properties or methods (other than those explicitly listed as being okay) from any thread other than the one which created the control's "handle" (usually there's just one UI thread)
  • Don't block the UI thread for any significant length of time, or you'll make the application unresponsive

In order to interact with the UI from a different thread, you need to "marshall" the call to the UI thread, using a delegate and calling Control.Invoke/BeginInvoke. You can test whether or not you need to call Invoke using the InvokeRequired property, but these days I personally tend to just do it anyway - there's not much penalty for invoking when you don't need to.

Lambda expressions in C# 3 (or anonymous methods in C# 2) make this a lot more pleasant as well.

For instance, you could use:

cbFly.Invoke((MethodInvoker)(() => cbFly.Items.Clear()));

All the brackets get in the way a bit, so you might want to add an extension method like this, if you're using C# 3:

public static void Invoke(this Control control, MethodInvoker action)
{
control.Invoke(action);
}

Then you could do:

cbFly.Invoke(() => cbFly.Items.Clear());

which is a good deal simpler. Usually you can get away with using a MethodInvoker by capturing any variables you need to access within the delegate.

See my threading tutorial or Joe Albahari's for more details.

As a secondary matter, I see you're using Thread.Abort - in fact on your own thread, despite it having other calls after it. Why? Aborting any thread other than your own is an "emergencies only" type call (which should usually be followed by the app being unloaded anyway) and I can't see any reason to abort the current thread when there's still work to be done afterwards...

type-safe Control.Invoke C#

Instead of passing the arguments to Invoke, pass them as a closured variable within the delegate.

So, instead of this:

Form.ActiveForm.Invoke(Form1.d, new object[] { "SecondThreadFunc counter: " + counter.ToString() });

Write this:

Form.ActiveForm.Invoke
(
new Action
(
() => Form1.d("SecondThreadFunc counter: " + counter.ToString())
)
);

This avoids the problem of passing arguments to Invoke and eliminates the type-safety issue.

If that seems a little wordy to you, you can also define a simple helper extension method:

static class Helpers
{
static public void MyInvoke(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
}

Now all you have to write in your main form is this:

this.MyInvoke( () => addText("SecondThreadFunc counter: " + counter.ToString()));

You can now get rid of all that SafeCallDelegate stuff and just pass whatever lambda you want when you need it.

Control.Invoke is hanging

There's only one reason a Control.Invoke() call would hang. Or a BeginInvoke() call not executing its target, same thing. It happens when the main thread of the program, the UI thread, is not idle and busy doing something else.

What the "something else" could be is all over the map. The worst thing you could do have the main thread wait for the worker thread to complete. That's a guaranteed deadlock if you use Invoke().

The condition is very easy to diagnose, use Debug + Break All and Debug + Windows + Threads. Double-click the Main thread and look at the Call Stack window. The top of the stack trace should say "Managed to Native Transition", the one below it should be FPushMessageLoop(). If you see something else then you've found the code that causes the deadlock.

Using Control.Invoke in an optimal way

You usually assign a delegate by directly passing it a function (which must match the delegate signature!):

MyCrossThreadDelegateInstance += invokeMe;

or

new MyCrossThreadDelegate(invokeMe);

Check this:
Youre on different thread and would like to update the TreeControl using your invokeMe() method.

private void invokeMe()
{
MyTree.BeginUpdate();
}

Due to this call on MyTree.BeginUpdate() is coming from a different thread, crossthread exception is thrown.
To prevent this we modify our invokeMe() method to avoid throwing the exception:

private void invokeMe()
{
if (MyTree.InvokeRequired)
MyTree.Invoke(new CrossThreadDelegate(invokeMe);
else
MyTree.BeginUpDate();
}

Before invoking u check if invoke is required - this is the case when u try to access a control from a different thread then the one the control was created on. This way it tries to find the thread which owns and created the control by bubbling up you thread tree.
If Control.InvokeRequired returns true, the same method (passed over by the delegate) is called again from the next thread. This is repeated until the owning thread is found. Now Control.InvokeRequired returns false and your ELSE-block is executed on the proper thread whithout throwing a crossthread exception.
For more details see MSDN Control.Invoke

There is no need to declare anything static except you want your delegate to be available in a global scope.

Edit: If you would use the BackgroundWorker like it was meant to be, the ProgressChanged event would do the job since this event is risen on the proper thread (UI thread). This event is fired by calling the BackgroundWorker.ReportProgress() member. See MSDN - BackgroundWorker class for more details

Invoke(Delegate)

The answer to this question lies in how C# Controls work

Controls in Windows Forms are bound to a specific thread and are not
thread safe. Therefore, if you are calling a control's method from a
different thread, you must use one of the control's invoke methods to
marshal the call to the proper thread. This property can be used to
determine if you must call an invoke method, which can be useful if
you do not know what thread owns a control.

From Control.InvokeRequired

Effectively, what Invoke does is ensure that the code you are calling occurs on the thread that the control "lives on" effectively preventing cross threaded exceptions.

From a historical perspective, in .Net 1.1, this was actually allowed. What it meant is that you could try and execute code on the "GUI" thread from any background thread and this would mostly work. Sometimes it would just cause your app to exit because you were effectively interrupting the GUI thread while it was doing something else. This is the Cross Threaded Exception - imagine trying to update a TextBox while the GUI is painting something else.

  • Which action takes priority?
  • Is it even possible for both to happen at once?
  • What happens to all of the other commands the GUI needs to run?

Effectively, you are interrupting a queue, which can have lots of unforeseen consequences. Invoke is effectively the "polite" way of getting what you want to do into that queue, and this rule was enforced from .Net 2.0 onward via a thrown InvalidOperationException.

To understand what is actually going on behind the scenes, and what is meant by "GUI Thread", it's useful to understand what a Message Pump or Message Loop is.

This is actually already answered in the question "What is a Message Pump" and is recommended reading for understanding the actual mechanism that you are tying into when interacting with controls.

Other reading you may find useful includes:

What's up with Begin Invoke

One of the cardinal rules of Windows GUI programming is that only the
thread that created a control can access and/or modify its contents
(except for a few documented exceptions). Try doing it from any other
thread and you'll get unpredictable behavior ranging from deadlock, to
exceptions to a half updated UI. The right way then to update a
control from another thread is to post an appropriate message to the
application message queue. When the message pump gets around to
executing that message, the control will get updated, on the same
thread that created it (remember, the message pump runs on the main
thread).

and, for a more code heavy overview with a representative sample:

Invalid Cross-thread Operations

// the canonical form (C# consumer)

public delegate void ControlStringConsumer(Control control, string text); // defines a delegate type

public void SetText(Control control, string text) {
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself
} else {
control.Text=text; // the "functional part", executing only on the main thread
}
}

Once you have an appreciation for InvokeRequired, you may wish to consider using an extension method for wrapping these calls up. This is ably covered in the Stack Overflow question Cleaning Up Code Littered with Invoke Required.

There is also a further write up of what happened historically that may be of interest.

Control.Invoke method not returning

I assume you're calling Parallel.Invoke from the UI thread. If so, that's the problem.

Parallel.Invoke blocks until all the calls finish... which means you're blocking the UI thread. The tasks you're starting can't complete, because Control.Invoke blocks until the call on the UI thread has finished - so your subtasks are waiting for the UI thread to become available in order to update the UI, but your UI thread is waiting for all the subtasks to finish.

You could just fix this by using BeginInvoke instead of Invoke (and you may want to do that anyway) but it's fundamentally a bad idea to use Parallel.Invoke in a UI thread, precisely because it blocks.



Related Topics



Leave a reply



Submit