If you're a windows developer and have done any dabbling in multithreading, chances are you've come across the "golden rule" that
controls must always be accessed on the thread from which they were created. One of the easiest ways to ensure this is to use the Invoke() method built into every control, passing it a delegate to a method you want executed on the proper thread.
Well, Control.Invoke can be awfully inefficient due to its use of DynamicInvoke (which is needed because it has no static understanding of the signature of the delegate you are passing into it, i.e. its parameters/return type). However, as I just discovered, the .NET makers added a little trick that you can use to boost performance of a Control.Invoke significantly.
The trick is that while Control.Invoke accepts a simple Delegate, which could be anything, it does special checks to see if is an instance of one of two specially-recognized delegate types. If so, Control.Invoke invokes that delegate statically, saving you much overhead.
The two "special" delegates are MethodInvoker ( void () ) and EventHandler ( void ( object, EventArgs ) ). Using either of these special delegate types will result in a major speed boost, even compared to an identically-defined delegate type.
Before we get to the numbers, here is how I tested it:
public delegate void NonStandardDelegate( object sender, EventArgs e );
const int numRuns = 40;
public static void ControlInvokeTest() {
List<double> times = new List<double>( numRuns );
for ( int k = 0; k < numRuns; k++ ) {
Form form = new Form();
IntPtr handle = form.Handle;
DateTime p1 = DateTime.Now;
for ( int i = 0; i < 100000; i++ ) {
form.Invoke( new EventHandler( delegate( object sender, EventArgs e ) {
int j = 10;
j++;
} ), null, null );
}
//MessageBox.Show( ( DateTime.Now - p1 ).ToString() );
times.Add( ( DateTime.Now - p1 ).TotalMilliseconds );
Console.Beep( 1000, 30 );
}
double avg = 0;
StringBuilder results = new StringBuilder();
foreach ( double time in times ) {
avg += time;
results.AppendLine( TimeSpan.FromMilliseconds( time ).ToString() );
}
avg /= numRuns;
results.AppendLine( "\nAverage: " + TimeSpan.FromMilliseconds( avg ).ToString() );
MessageBox.Show( results.ToString() );
}
I performed the tests in three ways. What you see above, as well as EventHandler replaced with (the identically-defined) NonStandardDelegate, and then also with MethodInvoker, which required that I get rid of the parameters.
And now for the numbers. EventHandler and MethodInvoker were comparatively identical, but not so with the dynamically-invoked NonStandardDelegate:
EventHandler
0.386 s
0.390 s
0.400 s
0.369 s
MethodInvoker
0.397 s
0.376 s
0.383 s
NonStandardDelegate
2.17 s
2.09 s
2.15 s
2.05 s
So, as you can see, simply using one of the "recognized" delegate types when calling Control.Invoke can increase invocation speed by five and a half times! That's nothing to sneeze at, especially if your application makes heavy use of concurrency and Control.Invoke to update the UI on a regular basis.
Happy programming!