【问题标题】:Using Tasks but the UI is still blocked使用任务但 UI 仍然被阻止
【发布时间】:2016-02-04 20:39:09
【问题描述】:

我有一个 WPF MVVM c# 应用程序。在我的一个观点中,我必须在单击按钮后从数据库中检索数据。 对数据库的调用真的很长,所以我想在调用时启用微调器,并在调用完成时禁用它。

所以在处理按钮单击的方法中(使用命令,而不是后面的代码)我设置布尔变量以启用微调器,然后创建一个 Task 来获取数据并将其设置为 ObservableCollection<T> 像这样

private void GetDataButtonCommand() {
    this.IsBusy = true;
    var tasks = new List<Task>();

    tasks.Add(Task.Factory.StartNew(() =>
    {
        System.Windows.Application.Current.Dispatcher.BeginInvoke(new System.Action(() =>
        {
            var GotData= DBHelper.GetData();
            _observablecollectionname.Clear();
            foreach (var item in GotData)
            {
                _observablecollectionname.Add(item);
            }
        }));
    }));

    var finalTask = Task.Factory.ContinueWhenAll(tasks.ToArray(), datacomplete =>
    {
        this.IsBusy = false;  
    });
}

Task 完成后,我想禁用微调器。

我遇到的问题是在通话期间我看不到微调器。 UI 仍然被阻止,即使你使用Task 来执行数据库查询。

我做错了什么?

【问题讨论】:

  • 绘制en.wikipedia.org/wiki/Swim_lane。拿纸清单绘制 2 个车道“UI 线程”,“其他线程”。比将您的代码的每一行映射到其中一个通道......如果这还不足以找到问题,它至少应该向您澄清您不知道的内容。
  • 任务是否在后台线程中执行?此代码中没有 async/await。对我来说,它看起来像是在 UI 线程上运行的。或者至少没有从后台线程返回的意义。
  • ContinueWithContinueWhenAll 等价于 C# 5.0 中的 await 调用
  • @Tseng 又学到了一些东西 ;)

标签: c# wpf multithreading mvvm


【解决方案1】:

您的代码正在 UI 主题上运行。

您在System.Windows.Application.Current.Dispatcher.BeginInvoke(new System.Action(() =&gt; { ... })) 中执行所有操作,因此很自然地会阻止 UI。

只有在确实需要执行 UI 操作时才应该调用调度程序。

var GotData= DBHelper.GetData(); 移到调度员之前。

但最好使用 C# 5.0 及更高版本的async/await 模式/关键字。

【讨论】:

    【解决方案2】:

    当我有一个长时间运行的查询时,我总是使用下面的代码。

        private async void button1_Click(object sender, EventArgs e)
        {
            var list = await GetDatabaseAsync();
            //do whatever you need with the list.
        }
    
    
    
        private async Task<List<string>>  GetDatabaseAsync()
        {
            var list = Task.Run(() =>
            {
                var _observablecollectionname = new List<string>();
                var GotData = Helper.GetData();
                _observablecollectionname.Clear();
                foreach (var item in GotData)
                {
                    _observablecollectionname.Add(item);
                }
                return _observablecollectionname;
            });
            return await list;
        }
    

    【讨论】:

    • 这里的主要问题是,您无法从线程操作可观察的集合。它必须通过并且您在示例中使用列表来完成。
    • List 可以更改为 List 之类的任何内容。这个问题有点模棱两可。如果只有一个列表,则上述代码可以正常工作。但是如果有多个列表,那么还有另一种方法。
    • 问题是关于 WPF 和ObservableCollection。它不是一个普通的列表或集合,它是一个实现INotifyCollectionChanged 接口的对象,用于在集合更新时通知 WPF UI。而且您只能从 UI 线程中添加/删除项目。如果您尝试从非 UI 线程执行此操作,则会出现异常。见stackoverflow.com/a/2104750/455493。附带说明一下,在 MVVM 中我们也不注册事件处理程序,但这是另一回事
    猜你喜欢
    • 2012-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多