Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Dispatcher Invoke(…)与BeginInvoke(…)混淆_C#_Multithreading_Invoke_Dispatcher_Begininvoke - Fatal编程技术网

C# Dispatcher Invoke(…)与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

我不明白为什么我不能在Count()方法中使用我的调度程序上的“BeginInvoke”使这个测试计数器应用程序与2个(或更多)同时运行的CounterTextBox一起工作

您可以通过使用调用替换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;
    }
}

当您使用
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();
}