Dispatcher Invoke(...) vs BeginInvoke(...) confusion
When you use Dispatcher.BeginInvoke
it means that it schedules the given action for execution in the UI thread at a later point in time, and then returns control to allow the current thread to continue executing. Invoke
blocks the caller until the scheduled action finishes.
When you use BeginInvoke
your loop is going to run super fast since BeginInvoke
returns right away. This means that you're adding lot and lots of actions to the message queue. You're adding them much faster than they can actually be processed. This means that there's a long time between when you schedule a message and when it actually gets a chance to be run.
The actual action that you're running uses the field _number
. But _number
is being modified by the other thread very quickly and while the action is in the queue. This means that it won't display the value of _number
at the time you scheduled the action, but rather what it is after it has been continuing on in it's very tight loop.
If you use Dispatcher.Invoke
instead then it prevents the loop from "getting ahead of itself" and having multiple scheduled events, which ensures that the value that it's writing is always the "current" value. Additionally, by forcing each iteration of the loop to wait for the message to be run it makes the loop a lot less "tight", so it can't run as quickly in general.
If you want to use BeginInvoke
the first thing you really need to do is slow down your loop. If you want it to update the text every second, or ever 10ms, or whatever, then you can use Thread.Sleep
to wait the appropriate amount of time.
Next, you need to take a copy of _number
before passing it to the Dispatcher
so that it displays the value at the time you scheduled it, not at the time it is executed:
while (true)
{
if (_number++ > 10000)
_number = 0;
int copy = _number;
this.Dispatcher.BeginInvoke(new Action(() => UpdateText(copy))
, System.Windows.Threading.DispatcherPriority.Background, null);
Thread.Sleep(200);
}
private void UpdateText(int number)
{
this.Text = number.ToString();
}
Control.Dispatcher.BeginInvoke() and Control.Dispatcher.Invoke() order execution is confusing?
BeginInvoke
calls the Action
you pass to it asynchronously on the thread that is associated with the Dispatcher
while Invoke
calls that action synchronously.
In other words, Invoke
immediately executes what ever Action
you pass to it while BeginInvoke
puts the action you pass to it on the Dispatcher
queue which is like a list of the things the Dispatcher
is going to do but with no guarantee when that is going to happen or as soon as the dispatcher has finished doing the other things waiting on that queue.
So sometimes the Dispatcher
might be busy doing something else and puts the action you pass to BeginInvoke
on the queue's end until it can execute it, and then it executes whatever action you pass to Invoke
immediately and that is the reason for the order differences.
Confused by the behavior of Dispatcher.BeginInvoke()
So if I understand your question correctly, you're saying that this code works exactly the way you want, but you're just trying to understand how (and why) it works?
Here's how it works. First, your thread runs this code:
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
window = new Window();
window.ShowDialog();
}));
That queues your action on the main (UI) thread's dispatcher queue, and then returns immediately: your worker thread continues running.
When the Application first started up (typically via the compiler-generated code that initializes your App.xaml object, though you can also do it explicitly by calling Application.Run), it started its message loop, which goes something like this (pseudocode, very very simplified):
public class Application {
public void Run() {
while (!Exited && action = Dispatcher.DequeueAction())
action();
}
}
So at some point shortly after you queue the action, the UI thread will get around to pulling your action off the queue and running it, at which point your action creates a window and shows it modally.
The modal window now starts its own message loop, which goes something like this (again, very simplified):
public class Window {
public bool? ShowDialog() {
DisableOtherWindowsAndShow();
while (!IsClosed && action = Dispatcher.DequeueAction())
action();
EnableOtherWindowsAndHide();
return DialogResult;
}
}
Later, your worker thread runs this code:
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
window.Close();
}));
Again, your action is queued to the UI thread's dispatcher queue, and then the BeginInvoke call returns immediately and your worker thread continues running.
So sooner or later, the UI thread's message loop will get around to dequeuing and executing your action, which tells the window to close. This has essentially the same effect as the user clicking the title bar's "X" button, which of course is perfectly OK to do even when you're inside a modal dialog. This causes ShowDialog's message loop to terminate (because the window is now closed), at which point the dialog is hidden and the other windows are re-enabled, ShowDialog returns, your original (ShowDialog) action is complete and so returns, and control falls back to the original message loop in Application.Run.
Note that there's one dispatcher queue per thread, not one per message loop. So your "close" action goes into the same queue that your "show dialog" action did. It's a different piece of code doing the message-loop polling now (the one inside ShowDialog instead of the one inside Application.Run), but the basics of the loop are the same.
Application.Current.Dispatcher.BeginInvoke(action) VS. Application.Current.Dispatcher.Invoke(action) (WPF)
For your purpose, there is no difference between Invoke
and BeginInvoke
. Dispatcher
is a task scheduler associated with one specific thread. You can add tasks via Invoke
or BeginInvoke
to a prioritized queue of the Dispatcher
, the Dispatcher
will execute them one by one on the thread it associated with. But Invoke
will block caller thread until task was completed, so using it may has negative effect on your working thread.
Invoke and BeginInvoke
With Invoke the method gets executed and the application waits for it to complete.
With BeginInvoke the method is invoked Asychnronously and the application continues to execute while the method referenced in BeginInvoke is executed.
With BeginInvoke you need to call EndInvoke to get the results of the method you executed using BeginIvnoke.
You should not update GUI components in BeginXXX methods as they are run in another thread to the GUI thread, contrary to your Invoke method. You cannot access GUI components in a different thread to the GUI thread.
Hope this helps!
Related Topics
How to Login/Authenticate a User with ASP.NET MVC5 Rtm Bits Using Aspnet.Identity
.Net Configuration (App.Config/Web.Config/Settings.Settings)
Ways to Synchronize Interface and Implementation Comments in C#
ASP.NET Identity Cookie Across Subdomains
Check for Null in Foreach Loop
Ienumerable VS Ireadonlycollection VS Readonlycollection for Exposing a List Member
Conversion of a Decimal to Double Number in C# Results in a Difference
Checking If an Object Is a Number in C#
Catching Exceptions with "Catch, When"
Get Connectionstring from Appsettings.JSON Instead of Being Hardcoded in .Net Core 2.0 App
How to Download/Upload Files From/To Sharepoint 2013 Using Csom
Linq Syntax - Selecting Multiple Columns
Wpf Listview: Attaching a Double-Click (On an Item) Event
Httpclient Single Instance with Different Authentication Headers
Raise Events in .Net on the Main UI Thread