Page 5 of 5
All controls define the ISynchronizeInvoke Interface and these are the methods we have been using in this article. For simplicity let’s restrict ourselves to just an implementation of Invoke and also assume that any delegates to be so invoked have no parameters. The extension to parameters and to the other “invoke” methods isn’t easy but it is a lot easier after you have seen the example.
The class that implements the Invoke is a simple implementation of point with two properties, x and y.
public class point
public int x;
public int y;
We could store the details of the thread that created an instance of the point class but for an example it’s easier to create a new thread to act as a “point worker thread”:
Private Thread T;
that is, thread T is the one used to run any delegates that are invoked on the point instance.
If we wanted to use the thread that created the Point instance to run the delegate we would have to add code to store the threads identity and use it to run the delegate. In this case we can keep the thread ready to go because it is dedicated to doing nothing but servicing the instance of the Point class.
We need a member variable to hold the delegate to be invoked and an AutoResetEvent to use to start it when it has a delegate to invoke and to stop it when there is nothing to do:
static AutoResetEvent autoEvent;
We could have used the thread’s Suspend and Resume methods to start and stop the thread but these are marked as obsolete since .NET 3.5 and AutoResetEvent is the correct way to control a thread.
The point constructor has to create a delegate, in this case called doInvoke, that it can run on its thread to get the method that is being invoked run on that thread.
ThreadStart doInvoke=delegate ()
} while (true);
What happens in the doInvoke is that the thread is immediately suspended waiting to woken up by another thread setting the autoEvent object. That is the thread that is going to be used to Invoke the method is suspended to wait for a method to run.
When this happens we assume that there is a delegate stored in _d ready to be invoked. We have to use DynamicInvoke because we have no idea what the form of the delegate actually is and hence it has to be late bound.The DynamicInvoke method simply runs the delegate on the current thread i.e. it is like a dynamic call to the method that the delegate wraps.
The rest of the constructor creates autoEvent in an unset state and then creates and starts the thread T giving it the doInvoke delegate to run
autoEvent = new AutoResetEvent(false);
You can now see how this is all going to work but how it all fits together is intricate.
The doInvoke delegate only ever runs on thread Tand most of the time it is suspended and waiting to be woken up. When it is woken up it runs what ever delegate it finds in _d and then goes back to sleep again.
All that is missing is the Invoke method that does the setting of the delegate and the waking up of the thread:
public void invoke(Delegate d)
_d = d;
}while (T.ThreadState ==
The invoke method simply stores the delegate to be invoked and signals, using autoEvent, that thread T should start running. As invoke is blocking it then enters a loop to wait for the invoked delegate to complete and thread T to suspend itself again. You can see that to implement BeginInvoke as a non-blocking Invoke all you have to do is return immediately.
That’s all there is to the invoke implementation in the point class. All we need now is some code to try it out and this is very easy. First we create a point instance:
point p = new point();
We also need a delegate to invoke:
asyncTask update = delegate()
p.x = 10;
p.y = 20;
Finally we invoke it:
When the invoke returns, the x and y properties of the point object are indeed set to 10 and 20 – and this has been done by thread T running the update delegate.
The extension of these ideas to delegates that pass parameters is easy enough – just pass an object array as in the case of the standard Invoke.
The extension to non-blocking Invokes is more complicated because you have to allow for the possibility that multiple delegates will be queued for execution. Yes, you need to use a queue to store all of the pending delegates. This complicates matters because you have to control access to the queue and arrange for thread T to empty the queue each time it is woken up.
It is more complicated in the details but the principles are the same.
Getting the Invoke pattern and the Asynchronous Invoke pattern right can be difficult but if you are trying to supply objects which are going to be used by other people it can be very well worth it.
If you would like to be informed about new articles on I Programmer you can either follow us on Twitter, on Facebook , on Digg or you can subscribe to our weekly newsletter.