【问题标题】:How to not freeze UI when using async Task ConfigureAwait(true)使用异步任务 ConfigureAwait(true) 时如何不冻结 UI
【发布时间】:2020-05-10 04:14:39
【问题描述】:

我有一个 WPF (UI) 应用程序,它在初始化/加载时从 SQL 数据库中检索数据。 为了使应用程序的启动时间更快(并且不会因为加载数据而冻结),我尝试做一些事情来异步加载数据,但就我所期望的 UX 而言运气不佳。

基本上,用户界面仍会冻结(即使只是一小会儿)。

这是我尝试过的:

数据加载的方法用 async Task 标记。我像这样调用这个方法

// Note that i use ConfigureAwait(true) because the data is being loaded from the UI.
LoadData().CongigureAwait(true);

方法如下:

        private async void LoadData()
        {
            try
            {
                using (var context = new DataModel.BusinessData())
                {
                    var people= await context.People
                                            .ToListAsync()
                                            .ConfigureAwait(true);

                    foreach (var person in people)
                    {
                        this.People.Add(new PersonItem(person));
                    }
                }
            }
            catch (Exception ex)
            {
               throw;
            }
        }

现在,尽管此数据检索具有异步特性,但仍会在主线程(UI 线程)上进行处理。同样,这是从 UI(MVVM 模式)调用的,所以我需要它。

最终结果是这样的:

在启动期间我仍然看到短暂的 UI 冻结。

问题:

UI 线程如何检索和处理数据而不 冻结用户界面?当被处理回来时,这甚至可能吗 界面线程?

【问题讨论】:

  • ConfigureAwait(true) 是默认值,因此您不需要它。如果我不得不猜测,那个“人”列表上是否有数据绑定?该列表有多大,绑定到该列表的 UI 有多大?如果我不得不猜测,我会说 UI 并没有因为调用获取人员而冻结,而是 UI 正在努力处理一次添加的 PersonItem。
  • 就我而言,我只有 1-3 个 People 项目进来。
  • 请勿编辑您的问题以删除您最初询问的代码。问题必须保持原样,编辑以澄清问题或改进格式。如果您更改问题以使问题中唯一的内容是工作代码,那么该问题对任何未来的读者都是无用的,并且无法回答。坦率地说,无论如何,您提出的修复是有问题的,但如果您真的相信这是您自己问题的答案,请将新代码作为答案发布并自我接受该答案。
  • 有时是UI中的实际面板会导致冻结,如果您的UI可以使用它,请查看使用VirtualizingStackPanel,我们不知道您使用的是什么控件。并且在使用 MvvM 时,应该有一个实际的 vm 参与所有这一切,这是无处可寻的?

标签: c# wpf asynchronous mvvm async-await


【解决方案1】:

只有对ToListAsync 的实际调用是异步的。其余代码在 UI 线程上执行。

如果People 包含很多项目,渲染它们可能会很慢。

您可以尝试将 People 属性分配给新集合,而不是将项目逐个添加到数据绑定集合中:

private async Task LoadData()
{
    using (var context = new DataModel.BusinessData())
    {
        var people = await context.People.ToListAsync();
        this.People = await Task.Run(() => new ObservableCollection(people.Select(person => new PersonItem(person))));
    }
}

记得实现 INotifyPropertyChanged 并为 People 属性引发 PropertyChanged 事件。

您还可以尝试在后台线程上执行所有代码,以防ToListAsync 的实现实际上阻塞:

private async Task LoadData()
{
    await Task.Run(() =>
    {
        using (var context = new DataModel.BusinessData())
        {
            this.People = new ObservableCollection(context.People.Select(person => new PersonItem(person)).ToArray());
        }
    });
}

如果仍然很慢,您需要减少要显示的项目数量或使用更快的组件来呈现项目。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-07
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 2014-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多