Dispatcher Invoke(…)vs BeginInvoke(…)混淆

我很困惑为什么我不能让这个测试计数器应用程序使用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; } } 

当您使用Dispatcher.BeginInvoke它意味着它会在稍后的某个时间点调度给定的操作以在UI线程中执行,然后返回控制以允许当前线程继续执行。 Invoke阻止调用者,直到计划的操作完成。

当你使用BeginInvoke你的循环将以超快的速度运行,因为BeginInvoke返回。 这意味着您要向消息队列添加许多操作。 您添加它们速度比实际处理速度快得多。 这意味着您在计划消息与实际有机会运行之间需要很长时间。

您正在运行的实际操作使用字段_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(); }