Can anybody please explain this statement written on this link
Invoke(Delegate):
Executes the specified delegate on the thread that owns the control\'s underlying window handle.
Can anybody explain what this means (especially the bold one) I am not able to get it clearly
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 interupting 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 interupting a queue, which can have lots of unforseen 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 onwards 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.
A control or window object in Windows Forms is just a wrapper around a Win32 window identified by a handle (sometimes called HWND). Most things you do with the control will eventually result in a Win32 API call that uses this handle. The handle is owned by the thread that created it (typically the main thread), and shouldn\'t be manipulated by another thread. If for some reason you need to do something with the control from another thread, you can use Invoke
to ask the main thread to do it on your behalf.
For instance, if you want to change the text of a label from a worker thread, you can do something like this:
theLabel.Invoke(new Action(() => theLabel.Text = \"hello world from worker thread!\"));
If you want to modify a control it must be done in the thread in which the control was created. This Invoke
method allows you to execute methods in the associated thread (the thread that owns the control\'s underlying window handle).
In below sample thread1 throws an exception because SetText1 is trying to modify textBox1.Text from another thread. But in thread2, Action in SetText2 is executed in the thread in which the TextBox was created
private void btn_Click(object sender, EvenetArgs e)
{
var thread1 = new Thread(SetText1);
var thread2 = new Thread(SetText2);
thread1.Start();
thread2.Start();
}
private void SetText1()
{
textBox1.Text = \"Test\";
}
private void SetText2()
{
textBox1.Invoke(new Action(() => textBox1.Text = \"Test\"));
}
Invoke((MethodInvoker)delegate{ textBox1.Text = \"Test\"; });
In practical terms it means that the delegate is guaranteed to be invoked on the main thread. This is important because in the case of windows controls if you don\'t update their properties on the main thread then you either don\'t see the change, or the control raises an exception.
The pattern is:
void OnEvent(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(() => this.OnEvent(sender, e);
return;
}
// do stuff (now you know you are on the main thread)
}
this.Invoke(delegate)
make sure that you are calling the delegate the argument to this.Invoke()
on main thread/created thread.
I can say a Thumb rule don\'t access your form controls except from main thread.
May be the following lines make sense for using Invoke()
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
There are situations though you create a Threadpool thread(i.e worker thread) it will run on main thread. It won\'t create a new thread coz main thread is available for processing further instructions. So First investigate whether the current running thread is main thread using this.InvokeRequired
if returns true the current code is running on worker thread so call
this.Invoke(d, new object[] { text });
else directly update the UI control(Here you are guaranteed that you are running the code on main thread.)
It means that the delegate will run on the UI thread, even if you call that method from a background worker or thread-pool thread. UI elements have thread affinity - they only like talking directly to one thread: the UI thread. The UI thread is defined as the thread that created the control instance, and is therefore associated with the window handle. But all of that is an implementation detail.
The key point is: you would call this method from a worker thread so that you can access the UI (to change the value in a label, etc) - since you are not allowed to do that from any other thread than the UI thread.
Delegate are essentially inline Action
\'s or Func<T>
. You can declare a delegate outside the scope of a method which you are running or using a lambda
expression(=>
); because you run the delegate within a method, you run it on the thread which is being run for the current window/application which is the bit in bold.
Lambda example
int AddFiveToNumber(int number)
{
var d = (int i => i + 5);
d.Invoke(number);
}
It means that the delegate you pass is executed on the thread that created the Control object (which is the UI thread).
You need to call this method when your application is multi-threaded and you want do some UI operation from a thread other than the UI thread, because if you just try to call a method on a Control from a different thread you\'ll get a System.InvalidOperationException.