Dispatcher in WPF: Under the hood
Let's start with basics. WPF is a STA based application. The philosophy comes from COM world, where by doing STA, the developer is not worried about threading as the system will guarantee only one thread will execute the code. VB based components used to follow this model while C++ allowed one to write MTA (multi-threaded apartment) based components as well. Both had issues. STA model of VB caused performance issues and MTA model of C++ caused sleepless nights due to cross thread issues. Anyway, I am diverting a bit too much here!
WPF still follows STA mainly to be interoperable with the earlier Win32/MFC/Winform programming model. A WPF application as such can create and use as many threads as required (to do background processing), but the UI related work will always need to be managed by the main UI thread (also sometimes called as the primary thread or dispatcher). I had explained some of this in the context of background worker thread in .NET here. The tradition of associating the UI controls (or in general thread affinity for any object) has been around for many years. This restriction makes lot of sense for the controls that are being painted on the screen by the UI thread, as they will otherwise start to look really weird in case some other thread started to change their state (like text in textbox or items in listbox) while they were rendered on the screen.
If you were to hence design such a thread aware system, how would you do it? Easiest will be that each control knows where it belongs, and when its values are set, when it is about to be painted etc. it can verify and see if that is happening on the right thread. Enter DispatcherObject. As you can see from the inheritance hierarchy (previous link), this is right up in the hierarchy and that makes sense so that the support for finding the thread identity functionality is available to all derived objects. The important methods of this class are CheckAcess and VerifyAccess and both essentially do the same thing. The main difference is that CheckAccess returns a boolean while VerifyAccess throws an exception. Internally both offload the verification to the current dispatcher object and the dispatcher really only checks if the current thread on which it is running is the same as the thread on which it started off (has this value saved from earlier). The code is as below
return (this.Thread == Thread.CurrentThread);
Another interesting point to note is that VerifyAccess internally still uses CheckAccess, but converts the false return value to an exception.
So much for the DispatcherObject. Next comes Dispatcher itself. This is the main object involved in managing the work queue and getting the work items in that queue executed. The work queue is a priority queue and each work item has an associated priority defined by DispatcherPriority. These priorities aren't really unique to WPF and are followed for programs running on Windows OS in general. Needless to say Send is the highest priority. Normal is where most of the code executes. ApplicationIdle, SystemIdle typically get used when you want to work only when nothing else is happening. If you minimize an anti-virus full system scan, you will typically see a message that says that the scan will happen in background, without impacting system performance. What is happening there is that the SystemIdle priority is being used. Invalid priority is a no-brainer and is really like an error condition.
Let's spend a bit more time with priority queue. Again the question is how do you implement given this scenario of having a queue in which items can have different priorities? The .net implementation actually uses multiple internal queues called as chains. Each chain maps to a priority, so you can have 11 chains. A chain isn't created as soon as the dispatcher object is created. It gets created as and when work items of that priority are added (en-queued) in the priority queue. So the Normal priority chain is what gets created immediately as that is needed and so does the Render priority. Others get created over the course of program execution. This doesn't ends here. The main priority queue holds a linked list of all work items and also has a sorted key value pair list of these additional priority chains. New work items are added to both the main sequential queue and the appropriate chain based on priority. It is pretty much like a standard linked list implementation where each node has pointer to previous and next node. The main priority queue holds additional reference to the head and tail of the queue which helps in easy de-queuing of items for execution (from the head) and en-queuing of new work items (at the tail). You would see details of this in the figure below. For now, here's what relevant portion of the PriorityQueue looks like
internal class PriorityQueue<T>
private Stack<PriorityChain<T>> _cacheReusableChains;
private int _count;
private PriorityItem<T> _head;
private SortedList<int, PriorityChain<T>> _priorityChains;
private PriorityItem<T> _tail;
The methods of interest in the Dispatcher class are Dispatcher.BeginInvoke and Dispatcher.Invoke. You use them basically to get information to your main UI thread (from a background thread) and so that further processing happens on the right thread and we don't get cross thread exceptions. Let's however try to understand how these two really work internally.
The basic idea for both BeginInvoke and Invoke is the same. We need to somehow get to the main UI thread for further processing. We already also know the convention that BeginInvoke provides async behavior while Invoke provides synch behavior. What this means is that at the point where you make a call to Invoke, the code will not execute further, till Invoke returns. On the other hand BeginInvoke will do some magic internally and then return and allow the code to execute further. Let's spend some time to understand how these both work.
Let's first understand BeginInvoke. Internally this method offloads the execution to BeginInvokeImpl, which is an internal method. All overloads of BeginInvoke essentially do the same. If you use an overload without the DispatcherPriority parameter, it is set to Normal priority internally else it will entertain what you asked for. It will obviously not allow you to set priority to say Send, as that is for a synchronous call. The main part of interest of this method implementation is as below
data = new DispatcherOperation(this, method, priority, args, isSingleParameter);
data._item = this._queue.Enqueue(priority, data);
The key points to note here is that the "method" is the method delegate that needs to be called and this is being queued up in the priority queue (_queue). Having done this, the BeginInvoke method call returns and thus you are able to see the asynchronous behavior. The method will be executed when it gets picked up from the queue based on its priority.
Invoke method is a bit more involved and needs a bit more understanding else you may get surprises when using it. Like BeginInvoke, this one (along with other Invoke overloads) calls and internal method called InvokeImpl. Default value of Normal priority and time of -1 millisecond is used if not provided. The priority set during this call really decides the exact implementation logic. The pseudo algorithm is something like this
If (priority == Send)
Create Synchronization context
Wait for operation to complete
If the priority is Send, which is where you would mostly want to use this, it gets the current synchronization context tries to set it, which is pretty much like taking control of the execution context. It uses the constrained execution region concept and kind of hijacks the execution by forcing a context switch. This uses an internal structure called SynchronizationContextSwitcher that pushes the current context to a saved context place holder, uses the new context provided as the current context and then executes the code. I wanted to provide a link for further reading on SynchronizationContextSwitcher, but surprisingly didn't get any MSDN documentation on it. You may however find this article useful.
The summary of this context switching and all that is that when using Invoke with Send priority, it will grab the current execution context, swap it out, do the processing (hence synchronous), reset the context and bail out. Phew! Summary, your callback method executes and your work is done.
The else part above shows that if the priority is not set to Send, then it pretty much follows the BeginInvoke logic. However the underlying idea of Invoke is that is a blocking call and hence after doing the BeginInvoke, it spins of an event to wait on it. The wait will be timed based on the time value specified and the default value of -1 means infinite wait. So if you don't set timer and end up doing something foolish in your Invoke call, you could end up with a hung application.
This brings me to the end of this blog. But before I close, I wanted to put all of this in perspective and hopefully this image will help you. I have explained it below using the numbers as seen in the image.
1. Normal code execution happens using main UI thread and the dispatcher priority queue. Internally this queue has multiple priority chains.
2. UI thread will de-queue a work item and process it, i.e. execute the relevant application code.
3. A background thread is spun-off for asynchronous operation
4. If it tries to do a direct code access (UI controls), it gets an exception
5. Instead do a BeginInvoke using dispatcher object
6. If can't wait for normal priority work and need to get things done right away, use Invoke with Send priority
That's all I had to share and btw if this all this sounded a bit too much to digest and worry about, .net made your life very simple. You can simple use BackgroundWorker class and do all the long work in the DoWork method. The RunWorkerCompleted event that is raised after this is already raised on the UI thread and you can happily go ahead and do all UI updates without fear of cross thread exceptions.