C# Dispatcher Invoke(…)与BeginInvoke(…)混淆
我不明白为什么我不能在Count()方法中使用我的调度程序上的“BeginInvoke”使这个测试计数器应用程序与2个(或更多)同时运行的CounterTextBox一起工作 您可以通过使用调用替换BeginInvoke来解决此问题。但这并不能解决我的困惑 下面是我所说的示例代码:C# Dispatcher Invoke(…)与BeginInvoke(…)混淆,c#,multithreading,invoke,dispatcher,begininvoke,C#,Multithreading,Invoke,Dispatcher,Begininvoke,我不明白为什么我不能在Count()方法中使用我的调度程序上的“BeginInvoke”使这个测试计数器应用程序与2个(或更多)同时运行的CounterTextBox一起工作 您可以通过使用调用替换BeginInvoke来解决此问题。但这并不能解决我的困惑 下面是我所说的示例代码: public class CounterTextBox : TextBox { private int _number; public void Start() { (new
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线程中执行给定的操作,然后返回控制以允许当前线程继续执行<代码>调用阻止调用方,直到计划的操作完成
当您使用BeginInvoke
时,您的循环将快速运行,因为BeginInvoke
会立即返回。这意味着您正在向消息队列中添加大量的操作。您添加它们的速度比实际处理它们的速度快得多。这意味着,从您计划消息到它真正有机会运行之间有一段很长的时间
您正在运行的实际操作使用字段\u number
。但是,\u number
正在被另一个线程非常快速地修改,而操作正在队列中。这意味着它不会在您计划操作时显示\u number
的值,而是在非常紧密的循环中继续之后显示的值
如果改用Dispatcher.Invoke
,则可防止循环“超前”并具有多个计划事件,从而确保其写入的值始终为“当前”值。此外,通过强制循环的每个迭代等待消息运行,它使循环的“紧度”大大降低,因此通常不能运行得那么快
如果你想使用BeginInvoke
你真正需要做的第一件事就是放慢你的循环速度。如果您想让它每秒更新一次文本,或者每10毫秒更新一次文本,那么您可以使用Thread.Sleep
等待适当的时间
接下来,您需要先复制一份\u number
,然后再将其传递给调度程序
,以便它在您计划时显示值,而不是在执行时显示值:
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);
}
很好的描述+1,用于提醒人们复制跨线程边界传递的变量。@JesseChisholm这不是关于跨线程边界传递值,而是关于复制正在关闭的变量的值,当您需要关闭值而不是关闭变量的语义时。延迟某些代码执行的闭包不需要涉及多个线程,即使在这种特殊情况下发生了。OK。同意。另外需要考虑的是,
BeginInvoke
有时需要一个平衡EndInvoke
。有关更多详细信息,请参阅。在这种情况下不是这样的,因为控件.BeginInvoke
(因此表单.BeginInvoke
)被记录为一种特殊情况,不需要EndInvoke
调度程序
不实现ISynchronizeInvoke
。这个方法碰巧与那个方法同名,但语义上明显不同。现在我通过您的解释理解了Invoke和BeginInvoke的不同。很好的解释+1
private void UpdateText(int number)
{
this.Text = number.ToString();
}