【问题标题】:Dispatcher Invoke(...) vs BeginInvoke(...) confusionDispatcher Invoke(...) 与 BeginInvoke(...) 混淆
【发布时间】:2013-09-25 15:33:30
【问题描述】:

我很困惑为什么我不能在 Count() 方法中使用我的 Dispatcher 上的“BeginInvoke”使这个测试计数器应用程序与 2 个(或更多)同时运行的计数器文本框一起工作。

您可以通过将 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;
    }
}

【问题讨论】:

    标签: c# multithreading invoke dispatcher begininvoke


    【解决方案1】:

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

    当您使用BeginInvoke 时,您的循环将超级 快速运行,因为BeginInvoke 会立即返回。这意味着您要向消息队列添加 lotlots 的操作。您添加它们的速度 快于它们的实际处理速度。这意味着在您安排消息和它实际有机会运行之间有很长的时间。

    您正在运行的实际操作使用字段_number。但是_number 正在被另一个线程修改非常快并且当动作在队列中时。这意味着它不会在您安排操作时显示 _number 的值,而是在非常紧密的循环中继续执行之后的值。

    如果您改用Dispatcher.Invoke,则它可以防止循环“超前”并拥有多个预定事件,从而确保它写入的值始终是“当前”值。此外,通过强制循环的每次迭代等待消息运行,它使循环变得不那么“紧密”,因此它不能像一般情况下那样快速运行。

    如果您想使用BeginInvoke,您真正需要做的第一件事就是减慢循环速度。如果您希望它每秒或每 10 毫秒或其他任何时间更新文本,那么您可以使用 Thread.Sleep 等待适当的时间。

    接下来,您需要在将_number 传递给Dispatcher 之前复制一份_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);
    }
    

    private void UpdateText(int number)
    {
        this.Text = number.ToString();
    }
    

    【讨论】:

    • 优秀的描述。 +1 用于提醒人们制作跨线程边界传递的变量的副本。
    • @JesseChisholm 这不是关于跨线程边界传递一个值,而是当你想要关闭一个值而不是关闭一个变量的语义时复制一个正在关闭的变量的值.延迟执行某些代码的闭包不需要涉及多个线程,即使在这种特殊情况下发生这种情况。
    • 好的。同意。还有一点需要考虑的是BeginInvoke有时需要平衡EndInvoke。有关详细信息,请参阅:stackoverflow.com/questions/1274276/…。在这种情况下不是,因为Control.BeginInvoke(因此Form.BeginInvoke)被记录为不需要EndInvoke的特殊情况。
    • Dispatcher 没有实现ISynchronizeInvoke。该方法恰好与该方法具有相同的名称,但在语义上明显不同。
    • 现在我通过您的解释了解了 Invoke 和 BeginInvoke 的不同之处。很好的解释+1
    猜你喜欢
    • 1970-01-01
    • 2014-01-22
    • 1970-01-01
    • 1970-01-01
    • 2020-01-01
    • 2014-07-09
    • 1970-01-01
    • 2016-01-02
    • 1970-01-01
    相关资源
    最近更新 更多