C# Dispatcher Invoke(...) 与 BeginInvoke(...) 混淆

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19009174/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 13:52:45  来源:igfitidea点击:

Dispatcher Invoke(...) vs BeginInvoke(...) confusion

c#multithreadinginvokedispatcherbegininvoke

提问by Jerev

I'm confused why I can't make this test counter application work with 2 (or more) simultaneous running countertextboxes with the use of "BeginInvoke" on my Dispatcher in the Count() method.

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

You can solve the issue by replacing the BeginInvoke by an Invoke. But this doesn't solve my confusion.

您可以通过将 BeginInvoke 替换为 Invoke 来解决该问题。但这并不能解决我的困惑。

Here's the sample code I'm talking about:

这是我正在谈论的示例代码:

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;
    }
}

采纳答案by Servy

When you use Dispatcher.BeginInvokeit means that it schedulesthe given action for execution in the UI thread at a later point in time, and then returns control to allow the current thread to continue executing. Invokeblocks the caller until the scheduled action finishes.

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

When you use BeginInvokeyour loop is going to run superfast since BeginInvokereturns right away. This means that you're adding lotand lotsof actions to the message queue. You're adding them muchfaster than they can actually be processed. This means that there's a long time between when you schedule a message and when it actually gets a chance to be run.

当你使用BeginInvoke你的循环时,它会运行得非常快,因为它会立即BeginInvoke返回。这意味着你要添加很多很多的行动,以消息队列。您添加它们速度比实际处理它们速度快得多。这意味着在您安排消息和它真正有机会运行之间有很长的时间。

The actual action that you're running uses the field _number. But _numberis being modified by the other thread very quicklyand while the action is in the queue. This means that it won't display the value of _numberat the time you scheduled the action, but rather what it is after it has been continuing on in it's very tight loop.

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

If you use Dispatcher.Invokeinstead then it prevents the loop from "getting ahead of itself" and having multiple scheduled events, which ensures that the value that it's writing is always the "current" value. Additionally, by forcing each iteration of the loop to wait for the message to be run it makes the loop a lot less "tight", so it can't run as quickly in general.

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

If you want to use BeginInvokethe first thing you really need to do is slow down your loop. If you want it to update the text every second, or ever 10ms, or whatever, then you can use Thread.Sleepto wait the appropriate amount of time.

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

Next, you need to take a copy of _numberbefore passing it to the Dispatcherso that it displays the value at the time you scheduled it, not at the time it is executed:

接下来,您需要_number在将其传递给 the 之前获取一个副本,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();
}