Dispatcher Invoke(...) vs BeginInvoke(...) confusion
本文关键字:confusion BeginInvoke vs Dispatcher Invoke | 更新日期: 2023-09-27 18:32:50
我很困惑为什么我不能让这个测试计数器应用程序与 2(或更多(同时运行的计数器框一起工作,并在 Count(( 方法中的调度程序上使用"BeginInvoke"。
您可以通过将 BeginInvoke 替换为 Invoke 来解决此问题。但这并不能解决我的困惑。
这是我正在谈论的示例代码:
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();
}