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

时间:2013-09-25 15:33:30

标签: c# multithreading invoke dispatcher 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;
    }
}

1 个答案:

答案 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();
}