【问题标题】:DoEvents is used yet application is not responsive使用了 DoEvents 但应用程序没有响应
【发布时间】:2018-09-25 10:03:28
【问题描述】:

在我的应用程序中,我调用了一个更新软件的过程——它存储在它自己的类中。即使您出于某种原因,我也一直在少数地方写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

【问题讨论】:

  • 请阅读Use of Application.DoEvents()的答案。此外,线程在睡眠时不做任何事情。
  • 我让线程休眠的原因是看到标签的变化,没有发生

标签: vb.net


【解决方案1】:

我不明白您为什么在这些特定位置调用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 在某种程度上是一种绕过它的黑客方式(并且是一种危险的黑客行为)。每次调用 DoEvents 时,它都会将控制权返回给表单以处理它已排队执行的任何其他操作(例如重新绘制屏幕和处理用户输入),然后将执行返回给原始事件处理程序以便它可以继续它停止的地方。这意味着,每次调用 DoEvents 时,它都允许在该点重新绘制表单,但事件处理程序仍会阻止所有 DoEvents 之间的 UI。

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

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

较新的方法更简洁,更易于阅读,但更复杂一点,它是使用 AsyncAwait 关键字来创建所有长时间运行的方法以及调用它们的事件处理程序, 异步的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多