我很困惑为什么我不能让这个测试计数器应用程序使用Count()方法在我的Dispatcher上使用“BeginInvoke”的2个(或更多个)同时运行的反文本框。
您可以通过Invoke替换BeginInvoke来解决问题。但这并不能解决我的困惑。
以下是我正在谈论的示例代码:
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;
}
}
答案 0 :(得分:67)
当您使用Dispatcher.BeginInvoke
时,意味着它计划在稍后的某个时间点在UI线程中执行的给定操作,然后返回控件以允许当前线程继续执行。 Invoke
阻止调用者,直到计划的操作完成。
当您使用BeginInvoke
时,您的循环会快速运行超级,因为BeginInvoke
会立即返回。这意味着您要将 lot 和 lot 操作添加到邮件队列中。您要比实际处理它们更快地添加它们 。这意味着您在安排消息与实际有机会运行之间需要很长时间。
您正在运行的实际操作使用字段_number
。但是,当动作在队列中时,_number
正被其他线程非常快地和修改。这意味着它不会在您安排操作时显示_number
的值,而是在它继续进行非常紧密的循环之后显示的内容。
如果你使用Dispatcher.Invoke
代替它,它会阻止循环“超越自身”并拥有多个预定事件,这可以确保它所写的值始终是“当前”值。此外,通过强制循环的每次迭代等待消息运行,它使循环不那么“紧”,因此它通常不能快速运行。
如果你想使用BeginInvoke
,你真正需要做的第一件事就是减慢循环速度。如果您希望它每秒更新文本,或者每隔10毫秒,或者其他任何内容,那么您可以使用Thread.Sleep
等待适当的时间。
接下来,您需要先复制_number
,然后再将其传递给Dispatcher
,以便它在您安排时显示值,而不是在执行时显示:
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();
}