I'm confused why I can't make this test counter application work with 2 (or more) simultaneous running countertextboxes with the use of "BeginInvoke" on my Dispatcher in the Count() method.
You can solve the issue by replacing the BeginInvoke by an Invoke. But this doesn't solve my confusion.
Here's the sample code I'm talking about:
public class CounterTextBox : TextBox
{
private int _number;
public void Start()
{
(new Action(Count)).BeginInvoke(null, null);
}
private void Count()
{
while (true)
{
if (_number++ > 10000) _number = 0;
this.Dispatcher.BeginInvoke(new Action(UpdateText), System.Windows.Threading.DispatcherPriority.Background, null);
}
}
private void UpdateText()
{
this.Text = "" + _number;
}
}
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 sinceBeginInvoke
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 useThread.Sleep
to wait the appropriate amount of time.Next, you need to take a copy of
_number
before passing it to theDispatcher
so that it displays the value at the time you scheduled it, not at the time it is executed: