Tuesday, January 22, 2008
BeginInvoke, MethodInvoker and Anonymous methods
It's no secret...all WinForms programmers know that you can only access most properties and methods of a control from the thread that created the control; typically this is the main UI thread. If you attempt to access a control's property or method from another thread, you will get an
Typically, the programmer would check
Many programmers choose to re-invoke the same method, as shown in the example below.
But, there is no requirement to have to invoke the same method. Many programmers choose to invoke a seperate method. It takes a bit more code, but invoking a different method can be a little better performance-wise because it allows you to do some stuff on the calling thread before re-invoking onto the main UI thread.
In either case, you see that
Starting with .NET 2.0, there is actually an easier way to do the
This "trick" works because anonymous methods have access to the "outer variables", which are the variables available in the parent's scope. In the example above,
NOTE: If you are going to use the MethodInvoker / anonymous delegate trick above, you should really read one of my other post on the scope of bariables entitled "For Loops, Variable Scope and Anonymous Delegates with Outer Variables".
That's really all I had intended for this post, but there are a few side details I thought I would point out while I was on the subject
InvalidOperationException that says "Cross-thread operation not valid:". [ Actually, you only get that exception on .NET 1.1 and later; .NET 1.0 would just lockup. ]Typically, the programmer would check
InvokeRequired on the form or control, and do a BeginInvoke if the code had to be scheduled to run on the appropriate thread.Many programmers choose to re-invoke the same method, as shown in the example below.
// Declare a delegate to be used with BeginInvoke
delegate void MyDelegate(int a, int);
// Run the method
void MyMethod1(int a, int b)
{
if (this.InvokeRequired)
{
// Reinvoke the same method if necessary
BeginInvoke(
new MyDelegate(MyMethod1),
new object[]{a, b} );
}
else
{
// Do Whatever you need to do here
...
}
}
But, there is no requirement to have to invoke the same method. Many programmers choose to invoke a seperate method. It takes a bit more code, but invoking a different method can be a little better performance-wise because it allows you to do some stuff on the calling thread before re-invoking onto the main UI thread.
// Run the method
void MyMethod2(int a, int b)
{
// Do some calculations why still on the calling thread
int y = 2 * a + b;
int z = 4 * b + y;
// Call a different method, passing in the calculated values
if (this.InvokeRequired)
{
// BeginInvoke to schedule it on the main UI thread
BeginInvoke(
new MyDelegate(MyMethod2_Internal),
new object[]{y, z} );
}
else
{
// Already on the main UI thread -- just call the method
MyMethod2_Internal(y, z);
}
// Method that does all the work
private void MyMethod2_Internal(
int m,
int n)
{
// Do Whatever you need to do here
...
}
}
In either case, you see that
BeginInvoke requires an instance of a delegate to be invoked and an object array of the parameters to be passed in. If your method is an event handler, there is most certainly already a delegate defined or you would not have been able to attach to the event. But, if your method is not an event handler, then you end up having to define a delegate such as MyDelegate in the examples above.Starting with .NET 2.0, there is actually an easier way to do the
BeginInvoke using a MethodInvoker and an anonymous method without having to define a separate delegate. This is shown below.
// Run the method
void MyMethod1b(int a, int b)
{
if (this.InvokeRequired)
{
// Reinvoke the same method if necessary
BeginInvoke( new MethodInvoker( delegate() { MyMethod1b(a, b); } ) );
}
else
{
// Do Whatever you need to do here
...
}
}
This "trick" works because anonymous methods have access to the "outer variables", which are the variables available in the parent's scope. In the example above,
a and b are outer variables, and are therefore available inside the anonymous method even though they are not passed as parameters [ I did not do delegate( int a, int b) ]. Since the anonymous method does not take parameters, the pre-defined MethodInvoker delegate can be used without having to create a new delegate. And, as an added bonus, the variables do not have to be placed in an object, so there isno boxing and unboxing of value-type parameters.NOTE: If you are going to use the MethodInvoker / anonymous delegate trick above, you should really read one of my other post on the scope of bariables entitled "For Loops, Variable Scope and Anonymous Delegates with Outer Variables".
That's really all I had intended for this post, but there are a few side details I thought I would point out while I was on the subject
- There are only four things on a control that can always safely be accessed from another thread:
InvokeRequired,Invoke(),BeginInvoke()andEndInvoke(). - The
CreateGraphics()method can also be safely accessed from another thread, but only after the Windows handle has been created. InvokeRequiredcan returnfalseeven though you are not on the main UI thread! Specifically, it will return false if it cannot find an appropriate Window handle by searching up the control chain starting with the control.- If
InvokeRequiredreturns false, you can callIsHandleCreatedto see if a control's Window handle has already been created. - Calling
BeginInvokebefore a control's Window handle has been created throws an exception. - If the Windows handle has not already been created, accessing a control's
Handleproperty causes the Window handle to be created on the thread that called the property. - Since
BeginInvokeis asynchronous, don't allow the method that is invoked to throw exceptions because they will be untrapped.
Comments:
<< Home
great trick using MethodInvoker thanks alot. .Great for cross-threading without having to explicitely declare a delegate.
Post a Comment
<< Home
Subscribe to Posts [Atom]
