Dispatcher.BeginInvoke

时间:2017-06-07 06:33:38

标签: c# wpf multithreading dispatcher

我有一段测试GUI和线程行为的代码。当我查询数据库并在DataGrid中添加大量行(10K +)时,我想保持 ProgressBar 动画运行(使用IsIndeterminate="True")。即使我将数据库和GUI代码包装在Dispatcher.BeginInvoke中, ProgressBar 动画也会在填充DataGrid时抖动。

我希望 ProgressBar动画能够冻结(如果在GUI线程上)或运行顺畅(如果在单独的线程上),但我无法理解动画为什么会急剧运行。

请不要建议 BackgroundWorker ,因为我想了解这个问题中的问题,以及为什么BeginInvoke没有分离线程。我只是循环遍历SqlDataReader并逐个添加到DataGrid作为Item而不是数据绑定到源或数据表。

// XAML
<Button Click="Button_Click"></Button>
<ProgressBar IsIndeterminate="True"></ProgressBar>
<DataGrid ... ></DataGrid>

// C#
private void Button_Click(object sendoer, RoutedEventArgs e)
{
    this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,(ThreadStart)delegate()
    {
        // Query database and update GUI (e.g. DataGrid)
    });
}

1 个答案:

答案 0 :(得分:3)

Dispatcher 始终在与其关联的线程(您的案例中为UI线程)上执行代码,无论您使用Invoke还是{{1} }(这是InvokeAsync的便捷简写)。因此,有关从数据库加载数据和更新BeginInvoke的所有工作都是在UI线程上完成的,因此动画并不流畅。

DataGridInvoke之间的区别在于前者是同步执行,而后者是异步执行。这意味着在第一种情况下,调用线程将被挂起,直到委托完成执行(即它将同步),而在第二种情况下,线程将继续执行而无需等待代表完成。让我试着用例子来指出这种差异。

示例I。从UI线程调用方法(如您的情况)

假设我们只有一个线程(UI线程)。调用InvokeAsync不会产生任何明显的影响,因为代理将立即执行,然后才会继续执行。所以这个:

Invoke

将具有与此相同的效果:

Dispatcher.Invoke(() => DoSomeStuff());
DoSomeOtherStuff();

然而,调用DoSomeStuff(); DoSomeOtherStuff(); 将产生这样的效果,即只有在执行了具有更高优先级(或已经安排具有相同优先级)的所有计划任务之后,才会安排委托执行。所以在这种情况下:

BeginInvoke

Dispatcher.InvokeAsync(() => DoSomeStuff()); DoSomeOtherStuff(); 将首先执行,DoSomeOtherStuff()秒。这通常用于事件处理程序,例如,只有在完全处理事件后才需要执行某些代码(例如see this question)。

示例II。从不同的线程调用方法

假设我们有两个线程 - UI线程和工作线程。如果我们从工作线程中调用DoSomeStuff()

Invoke

第一个Dispatcher.Invoke(() => DoSomeStuff()); DoSomeOtherStuff(); 将在UI线程上执行,然后DoSomeStuff()将在工作线程上执行。如果是DoSomeOtherStuff()

InvokeAsync

我们只知道Dispatcher.InvokeAsync(() => DoSomeStuff()); DoSomeOtherStuff(); 将在UI线程上执行,而DoSomeStuff()将在工作线程上执行,但它们执行的顺序是不确定的。*

当您的委托产生一些结果并且需要它继续在工作线程上执行时(例如,当您需要获取依赖项属性值时),通常会使用DoSomeOtherStuff()。另一方面,Invoke通常在委托不产生任何结果(或结果被忽略)时使用,例如在你的情况下 - 更新InvokeAsync不会产生任何值得等待的结果,所以你可以立即继续加载下一批数据。

我希望能为您解决这个问题,并且您可以看到为什么解决方案为&#34; jerky UI&#34;是将繁重的工作委托给另一个线程,只使用调度程序与UI进行交互。这是使用DataGridBackgroundWorker来自的建议。

*实际上它们可能会同时执行。我的意思是,如果两个方法只打印一些文本到控制台,控制台中的消息顺序是不确定的。