Backgroundworker - 在BGW完成之前,UI仍然没有响应

时间:2013-08-01 15:14:09

标签: vb.net multithreading winforms backgroundworker

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

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

据我所知,我必须将UI更改代码移动到ProgressChanged事件或RunWorkerCompleted事件,我已经完成了。我最初把所有代码都扔进了DoWork事件并且它非常快但发现它不安全所以我重新设计它以将所有与UI相关的oode移动到ProgressChanged。它现在不是那么快,看起来BGW控件在更改BGW_ProgressCHanged事件中的控件之前等待UI线程完成。当我在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

2 个答案:

答案 0 :(得分:2)

您在代码中删除了所有问题的证据,但诊断非常匹配。您有消防水管问题。您的代码经常调用ReportProgress。

当ProgressChanged事件处理程序需要更多时间而不是ReportProgress调用之间的时间时出现问题。这就像从消防水管中饮用,无论你吞水多快,都无法跟上潮流。

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

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

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

   System.Threading.Thread.Sleep(45)

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

像这样使用Sleep()是问题的一个丑陋的Q&amp; D解决方案,它当然也会减慢你的工作量,因此它可以减少工作量。您将不得不改进您的代码,以便不会发生这种情况,只需减少ReportProgress调用。

答案 1 :(得分:0)

在启动worker之前你可能想要添加一件事:

    Me.SuspendLayout()

然后,在RunWorkerCompleted事件中:

    Me.ResumeLayout()

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

修改

替换

BackgroundWorker1.ReportProgress(...)

使用

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

摆脱了synclock。

<强> EDIT2

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

开始这样的工作:

BackgroundWorker1.RunWorkerAsync(<globalvar>)

然后像这样访问:

Dim globalVarData As Object = e.Argument