The Invoke pattern |
Written by Mike James | ||||||
Thursday, 16 September 2010 | ||||||
Page 3 of 5
InvokeThe mechanism embodied in Invoke is a fairly general way of transferring calls to methods from one thread to another. Basically the idea is that thread 1 and thread 2 in our example should ask the UI thread to run a method which achieves the desired result.Thus threads 1 and 2 Invoke the UI thread to do work for them. To do this we first need a new delegate type: delegate void Delegate_addtext(string s); There are a number of ways of creating the delegate instance and an anonymous method is one way: public void CountUp() public void CountDown() Notice that the anonymous methods have to be defined within the form’s methods to give them the correct context and access to the form’s controls – don’t worry there is a much better way to do the job which is explained later. Currently we are just using the delegates to do the same job as before and the Textbox is still being accessed by two threads. If you run the program you will see the same problems as before. To run the delegate on the UI thread we need to change the calls to addtext to: this.Invoke(addtext, We are using object initialisers – to create an object used to pass the string parameter to the delegate. You have to pack all of the parameters in the correct order within an object array to be passed to the delegate which is invoked on the UI thread. Now if you run the program you will discover that its behaviour is quite different. Instead of missing values and the occasional strange character you get a rock solid complete list of all of the numbers produced by each of the threads. After an initial burst the pattern of thread access also settles down to thread 1, then thread 2, then thread 1, and so on because of the need for each thread to wait for the UI thread to be ready to process the Invoke. The Invoke method is synchronous and blocking. That is when thread 1, say, does: this.Invoke(addtext, it waits until the addtext delegate finishes running on the UI thread before proceeding. There is an asynchronous non-blocking form of the Invoke method – BeginInvoke. When called it starts the delegate running on the UI thread and returns immediately to allow the calling thread to continue. The calling thread can get on with other jobs until it detects that the delegate has finished its job. The simplest way of checking that the delegate has finished is to use EndInvoke to test the IAsynchResult object returned by the BeginInvoke. EndInvoke simply blocks until the delegate has finished. So, for example, you could use something like:
IAsyncResult dotext this. Self InvokeAlthough this demonstrates the basic invoke mechanism there is a very sophisticated way of doing the same job which is described in the documentation as part of an overlong example. It is so good it’s worth repeating in case you miss it. The idea is that you make use of the InvokeRequired method which compares the thread ID of the calling thread to the ID of the creating thread. If they are different then an Invoke is required but the clever part is the way the same method is used but restarted on the UI thread. For example: public void addtext2(string s) First we check to see if addtext2 is runing on the UI thread: if (this.textBox1.InvokeRequired) { If it isn’t running on the UI thread we wrap in in a delegate and use invoke to call the addtext2 method again but this time on the UI thread: Delegate_addtext d = If addtext2 was called on the UI thread, or has not been invoked on the UI thread, a simple assignment completes the task. else The For loop of each thread now just calls addtext2 without worrying about invoke or the UI thread: addtext2(i.ToString()); That is the new Addtext2 method works out if it should have been Invoked rather than called and if it needs and Invoke it wraps itself as a delegate and uses Invoke to run itself on the UI thread. If you want to give the UI thread time to process events then it’s a good idea to add: Application.DoEvents(); after updating the textbox - but only if you are working with Windows Forms because WPF uses a very different dispatch system and doesn't support anything like a DoEvents method. <ASIN:0470185481> <ASIN:0672330792> <ASIN:0470495995> <ASIN:0470596902> <ASIN:1449380344> <ASIN:0321578899> |
||||||
Last Updated ( Monday, 20 December 2010 ) |