【问题标题】:GUI freeze while loading data in BackgroundWorker在 BackgroundWorker 中加载数据时 GUI 冻结
【发布时间】:2011-02-09 11:10:46
【问题描述】:

我有一个ListView 控件从BackgroundWorkerReportProgress 接收数据。工人快速连续地倾倒了大约一百行。每个数据块都会触发ProgressChanged 事件,并且GUI 线程会向ListView 添加一个新项目。

由于数据检索操作在单独的线程上运行,因此 GUI 应该在每次更改时更新。但由于某种原因,它没有发生 - 界面保持冻结,直到工作人员完成。

有什么想法吗?

另外,测试是在非常快的机器上完成的,所以我认为计算机性能不是问题。

这是一个 WinForms 应用程序。

【问题讨论】:

  • 还提到 WPF 或 windows 窗体。
  • WinForms。该代码没有任何具体内容 - 只是 Worker.ReportProgress(25, newItem) 和 ListView1.Items.Add(item.DisplayName)。

标签: .net multithreading user-interface backgroundworker


【解决方案1】:

在 ReportProgress 调用之后将其放入您的 BGW 工作循环中:

 System.Threading.Thread.Sleep(15);

现在您看到 ListView 更新的可能性很大。这里发生的是 UI 线程充斥着委托调用请求。总是在绘制通知之前发送。如果 next 调用请求在前一个调用请求完成之前到达,那么它就永远无法完成正常的内务处理任务。就像绘画和响应输入一样。这些项目确实会添加到列表视图中,只是您看不到它正在完成。

减少调用 ReportProgress 的频率。每秒执行超过 25 次是浪费资源,没有人可以看到其中的差异。让 BGW 将项目存储在 List 中,并通过调用 AddRange() 减少 UI 线程开销。

【讨论】:

  • 那行得通。但是这个过程现在取决于PC的性能——慢机器延迟需要更高。有没有与性能无关的方法?我想也许 Application.DoEvents 会帮助等待内务处理完成,但现在得到 StackOverflowException(呵呵)。
  • 每秒25次是人类的数字,对于任何机器来说都足够了。在至少 40 毫秒过去之前不要调用 ReportProgress。 DateTime.Now 可以很好地衡量这一点,您每秒将获得约 21 次更新。
  • 我刚刚从大约一年前写的表格中取出这段代码,想着“我到底为什么要把它睡在这里??”阅读您对这个问题的回答,我想起了我为什么首先这样做。 “噢噢噢,对了。它需要时间来赶上上一个 ProgressChanged 事件。”
【解决方案2】:

您可以尝试使用 AddRange 而不是 Add 将项目批量加载到 ListView1.Items。只需填充一个中间数组并与 AddRange 一起使用。

【讨论】:

  • 这样做的主要目的是向用户显示数据,而无需等待整个加载完成。
  • @Sphynx - 我在考虑很多小批量。根据我的经验,Winforms listview 的性能不是很好。
【解决方案3】:

ListView 中逐行添加即使在快速机器上也会导致冻结。

尝试在填充过程之前和之后调用ListView.SuspendLayout()ListView.ResumeLayout()

【讨论】:

  • 刚刚尝试过 - 似乎没有帮助。我见过很多程序这样做——他们在另一个线程上加载一个标准的 ListView,每次更改都会更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 1970-01-01
相关资源
最近更新 更多