Introduction
The WPF developers cannot avoid using the Dispatcher class. When we have to access controls or other UI parts on other threads (not UI thread), we should use Dispatcher.InvokeAsync or Dispatcher.Invoke method. Also, Dispatcher can be used to delay a task until the current task is executed.
Developers often used Dispatcher.BeginInvoke in the past. And Microsoft adds a new method which’s name is Dispatcher.InvokeAsync in .NET Framework 4.5.
Are there any differences between these methods? And how does Dispatcher work?
Details
The original link is 深入了解 WPF Dispatcher 的工作原理(Invoke/InvokeAsync 部分). But this blog was written in Chinese, so I translate its content to English. Waterlv is an MVP(Microsoft Most Valuable Professional), and he is good at .NET Core\WPF\.NET. Here is his Blog.
BeginInvoke and InvokeAsync
BeginInvoke method has existed when the Dispatcher class is added in the .NET framework 3.0. Are you familiar with Begin prefix? Yes, it is the Begin\End asynchronous programming model(APM) which is introduced in .NET Framework 1.1. Although BeginInvoke is not completely implemented according to the APM model(there is no End and no IAsyncResult result), but this method is also thread-related. Not only does the name carry Begin for the means of asynchronous execution, but there are still ancient types like Delegate in the parameter list. All the details above show that the BeginInvoke method is an old API.
Every developer is still feeling excited about the async/await asynchronous model which was introduced in .NET Framework 4.5‘s updates. This model allows developers to write asynchronous codes as simple as synchronous codes. This is a new asynchronous programming model which is recommended by Microsoft, and its name is TAP (Task-based Asynchronous pattern). And Dispatcher.InvokeAsync was introduced in this version. So, what are the differences between BeginInvoke and InvokeAsync?
Differences
Firstly, we should read the source codes of these methods.
1 | [] |
There are five overload methods, and it is easy to find that three of them are marked by special attribute to make them invisible in IntelliSense list (but you can find them when you use ReSharper). Are there any differences among these methods? The answer is NO. After reading these methods’ code, we can find that they all call the same method named LegacyBeginInvokeImpl. Here is the code of the method.
1 | [] |
And its name’s prefix Legacy gives us some information about Microsoft’s altitude this method. It probably means Microsoft does not want to maintain this code anymore. Maybe, a few years later it will be marked as an Obsolete method.
Here is the code of InvokeAsync method.
1 | public DispatcherOperation InvokeAsync(Action callback); |
This is what the TAP code looks like. After reading these method’s code we can find that the implementation of these methods is almost the same. Here, I post the third method’s source code.
1 | [] |
What is the feeling when you read these codes? Yeah, there must be a voice in your mind and it says that ‘I read them before’. They are almost the same as LegacyBeginInvokeImpl.
Finally, we knew the answer to why Microsoft recommended InvokeAsync instead of BeginInvoke. Microsoft had changed BeginInvoke‘s implementation to TAP model but didn’t change its name and the parameters list in NET Framework 4.5. So, they added another six methods(InvokeAsync), and these all methods above do the same thing.
Due to InvokeAsync and BeginInvoke has the same codes, we don’t have to introduce both methods. So, in the following paragraphs, we are going to introduce more details about InvokeAsync.
Thoery of InvokeAsync
All information above told us that the core of the InvokeAsync method is InvokeAsyncImpl. Here are the steps of the InvokeAsync method.
Wrap the
Action/FuncwithDispatcherOperation.Therefore, the task and priority we passed will be processed together;Add
DispatcherOperationto a queue ofPriorityQueue<DispatcherOperation>. This queue implements aSortedListclass, every time it pops a task is depending on its priority.Call
RequestProcessing, send a message to a hidden window at last.When the hidden window received that message, it will take a task from the
PriorityQueueand execute it. (The real situation is more complicated, all details will be shown below).

In this progress, it calls TryPostMessage to send messages. Here is the method’s code.
1 | UnsafeNativeMethods.TryPostMessage(new HandleRef(this, _window.Value.Handle), _msgProcessQueue, IntPtr.Zero, IntPtr.Zero); |
It is important to notice that there is a _window, but what does this _window mean? After reading the constructor of the Dispatcher class, we found this _window. It is not the Window class we usually know, it a hidden window of Win32, and its usage is to send and receive the schedule message of the Dispatcher class. For more details about this window, visit MessageOnlyHwndWrapper‘s source code, and you will find it calls UnsafeNativeMethods.CreateWindowEx method to create this window in its base class HwndWrapper.
Since it can send a message to _window, it is natural to Hook its message handler, like this:
1 | // Create the message-only window we use to receive messages |
In this WndProcHook method, it handles three types of messages.
- Close the hidden window (
_window). - Handle the scheduled task of the
Dispatcherclass (this message is registered at the static constructor method ofDispatcher). - Timer.
Why does it handle the message about the timer?
After researching, it is clear that Microsoft split all priorities into three types.
- Foreground priority (from
DispatcherPriority.LoadedtoDispatcherPriority.Send, the number is 6 to 10). - Background priority (from
DispatcherPriority.BackgroundtoDispatcherPriority.Send, the number is 4 to 5). - Idle priority (from
DispatcherPriority.SystemIdletoDispatcherPriority.ApplicationIdle, the number is 1 to 3)
But, it needs to notice that the Background priority and Idle priority are treated as one type of priority in Microsoft’s code. So, in fact, there are just two types of priority, the Foreground and Background. And there is one point to distinguish them is user’s input.
If a user’s input occurs, a timer will be turned on, and during this time interval, all Background priority tasks will not be executed. But Foreground priority tasks will not be affected by the user’s inputs. Therefore, the user’s inputs will not be starved, and the WPF application will not be stuck from the input level.
It’s important to notice that InvokeAsync is implemented according to the TAP asynchronous model, and this method can be used with await. How does it work?
After reading the source code of InvokeAsync which is posted above, we noticed that the return value of the InvokeAsync method is an instance of DispatcherOperation. This instance is created at the beginning of the codes. DispatcherOperation operation = new DispatcherOperation(this, priority, callback); There is an Invoke method without a return value, but it will change the result of Task. And the DispatcherOperation class has implemented the GetAwaiter method so we can use await before this method.
For more information about await, see How to write a custom awaiter – Lucian’s VBlog.
The theory of Invoke
If you think the theory of Invoke is easier than InvokeAsync, probably you may ignore a very import question - deadlock. If the Invoke implemented according to the Synchronous wait method, the UI thread will be blocked when it calls the Invoke method on the current thread. And the Action passed by Invoke method will be executed on UI thread, but this thread is blocked, so deadlock happens.
To avoid deadlock, Invoke method must have other implementation, and there are two branches for this implementation developed by Microsoft.
If the task’s priority is the highest priority (the number is 10), it will call the
actionpassed byInvokemethod.If the task’s priority is lower than 10, it will call
DispatcherOperation.Waitmethod to wait.
Here is a part of the source code of DispatcherOperation.Wait method.
1 | public DispatcherOperationStatus Wait(TimeSpan timeout) |
In this method above, it calls Dispatcher.PushFrame, and this method can wait without blocking the thread. For more theories visit Learn more about how WPF Dispatcher works.(Part 2 - PushFrame).
Conclusion
- We recommended using
InvokeAsyncinstead ofBeginInvokeif your application’s framework version is higher than 4.5. Dispatcherexecute allInvoketasks by priority by creating a hidden message window.Invokemethod avoids blocking UI thread by thePushFramemethod.
References
- Asynchronous model
- InvokeAsync
- WPF message mechanism
- Awaiter