1

我为我的应用程序实现了一个 BGW,希望加快启动窗口的加载时间(40 多个控件)

我不会发布我的整个代码,因为它太长了,但会给你代码的要点。我将需要时间才能完成的大函数调用与少数控件分开,并将它们移到 BGW 中,希望异步加载控件以帮助加快进程。

据了解,我必须将 UI 更改代码移动到 ProgressChanged 事件或 RunWorkerCompleted 事件,我已经这样做了。我最初将所有代码都扔到 DoWork 事件中,它非常快,但发现它不安全,所以我对其进行了重新设计,以将所有与 UI 相关的 oode 移动到 ProgressChanged。现在几乎没有那么快了,而且 BGW 控件似乎要等到 UI 线程完成后再更改 BGW_ProgressCHanged 事件中的控件。当我对 DoWork 进行所有更改时,我从未见过两者之间的这种“滞后”。我能做些什么呢?或者我至少可以让 BGW 实时更新控件,而不是在更新所有控件之前等待所有控件完成?

响应速度较低,并且会锁定窗口以等待 BGW 控件更新。只是寻找可能发生的事情

Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
SyncLock <globalVar>
                    BackgroundWorker1.ReportProgress(0, "Tom")

End SyncLock
End Sub

 Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
  Dim value As String = DirectCast(e.UserState, String)
 Select Case e.ProgressPercentage

Case 0 
 lblName.text = value
 lblName.Visible = true
End Select
End Sub
4

2 回答 2

2

您删除了代码中问题的所有证据,但诊断结果非常匹配。你有消防水带问题。您的代码过于频繁地调用 ReportProgress。

当您的 ProgressChanged 事件处理程序需要的时间超过 ReportProgress 调用之间的时间时,事情就会出错。这就像从消防水管里喝水一样,无论你吞下水的速度有多快,你都跟不上水流。

这就是 UI 线程所经历的。当它完成对 ProgressChanged 事件处理程序的调用时,还有更多的水,还有另一个调用处理程序的请求。在 UI 线程无法跟上的情况下,这种情况会无情地继续下去。它现在不再履行其正常职责。这意味着您的 UI 停止更新,不再执行绘制。它不再响应输入。

这可能会在工作线程停止运行后持续一段时间,UI 仍在尝试处理积压的请求。当它最终到达那里时,UI 突然恢复生机。

诊断这种情况的一种简单方法是在 ReportProgress 调用之后添加此方法调用:

   System.Threading.Thread.Sleep(45)

这会减慢工作线程的速度,足以将 ReportProgress() 调用的次数限制为每秒不超过 21 次。这对于人眼来说已经足够快了,任何更快的东西看起来都像模糊,所以是浪费精力。如果这样可以解决问题,那么您就找到了原因。

像这样使用 Sleep() 是一个丑陋的 Q&D 解决方案,它当然也会减慢你的工作人员的速度,因此它完成的工作更少。你必须改进你的代码,这样就不会发生这种情况,并且只需要更少的 ReportProgress 调用。

于 2013-08-01T16:23:57.350 回答
0

在启动工作人员之前,您可能需要添加一件事:

    Me.SuspendLayout()

然后,在RunWorkerCompleted事件中:

    Me.ResumeLayout()

这应该暂停所有布局逻辑,直到所有工作完成,然后在 1 次操作中更新整个表单。

编辑

代替

BackgroundWorker1.ReportProgress(...)

DirectCast(sender, BackgroundWorker).ReportProgress(...)

并摆脱同步锁。

编辑2

将数据提供给DoWork事件的正确方法是通过DoWorkEventArgs.

像这样开始工作:

BackgroundWorker1.RunWorkerAsync(<globalvar>)

并像这样访问它:

Dim globalVarData As Object = e.Argument
于 2013-08-01T15:27:54.563 回答