使用了DoEvents,但应用程序没有响应

时间:2018-09-25 10:03:28

标签: vb.net

在我的应用程序中,我调用了一个更新软件的过程-该过程存储在其自己的类中。甚至出于某种原因,即使您在几个地方都写过Application.DoEvents(),更新表单中的标签仍未更新,并且表单本身处于非活动状态。

Namespace software

Public Class updater 

    Public Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Boolean 
        Application.DoEvents()
        Thread.Sleep(1000)

        frmUpdate.lblResult.Text = "Update is about to begin"
        Thread.Sleep(1000)
        frmUpdate.lblResult.Text = "Downloading data"
        Thread.Sleep(1000)

        Application.DoEvents()

        frmUpdate.lblResult.Text = "About to start the writing process" 
        Application.DoEvents()

        frmUpdate.lblResult.Text = "Software was updated, please restart your device."
    End Function

End Class

End Namespace

1 个答案:

答案 0 :(得分:6)

我不知道您为什么在这些特定位置致电DoEvents,因为它们在所在位置都不会产生任何明显的效果。第一个发生在任何标签更改之前,因此让表单刷新在那里毫无意义。在所有长时间运行的工作都已经完成(三个睡眠)之后,其他任务才结束。因此,尽管它们将允许表单在执行离开方法之前进行刷新,但无论如何它将很快离开该方法,因此也没有必要在此进行任何操作。甚至适用于调用DoEvents的唯一地方是在两个长时间运行的事物之间。例如,如果您这样做了,您会发现有所不同:

Public Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Boolean 
    Thread.Sleep(1000)
    frmUpdate.lblResult.Text = "Update is about to begin"
    Application.DoEvents()

    Thread.Sleep(1000)
    frmUpdate.lblResult.Text = "Downloading data"
    Application.DoEvents()

    Thread.Sleep(1000)
    frmUpdate.lblResult.Text = "About to start the writing process" 
    frmUpdate.lblResult.Text = "Software was updated, please restart your device."
End Function

您需要了解在.NET WinForms(以及WPF)中,UI在单个线程上运行。我的意思是,如果您的事件处理程序之一包含需要很长时间才能完成的代码,则UI将在事件处理程序执行的整个过程中被冻结。 UI刷新将完全被阻止,直到最后一个事件处理程序完成所有操作为止。 DoEvents某种程度上是解决该问题的一种hack方法(并且是一种危险的hack)。每次调用DoEvents时,它将控制权返回到表单以处理其排队等待做的其他事情(例如重新绘制屏幕和处理用户输入),然后将执行返回给原始事件处理程序,以便它可以从中断处继续。这意味着,每次您调用DoEvents时,它都可以在那一刻重新绘制窗体,但是事件处理程序仍会在所有DoEvents之间阻塞UI。

正如其他人已经暗示的那样,强烈建议不要使用DoEvents。它不仅效率较低,而且可能导致各种意外行为。在.NET之前的VB版本(例如VB6)中,DoEvents通常是唯一的选择,但是在.NET中,多线程相对容易。在某些情况下,DoEvents依旧仍然有用,但是它们应该很少而且相距甚远,只能使用great care and caution来实现。

有两种推荐的在WinForm应用程序中实现多线程的方法。仍然有效的原始方法是使用BackgroundWorker组件(您可以在WinForm设计器工具箱中找到它)。 BackgroundWorker在另一个线程上引发一个事件,以便您可以在该事件处理程序中完成所有长时间运行的工作,而不会阻塞UI。然后,当所有操作完成后,它将引发另一个事件,返回UI线程,以便您可以在工作完成后更新UI。

较新的方法更简洁易读,但更为复杂,它是使用AsyncAwait关键字来创建所有长期运行的方法和事件调用它们的处理程序,是异步的。