4

我正在寻找一种在长时间操作期间实现等待屏幕的通用方法。我以前使用过几次线程,但我觉得我实现它要么很差,要么太麻烦(和复制/粘贴——太可怕了!)。

我想让它尽可能通用和简单,这样我就不必实现大量的BackgroundWorkers 处理各种废话,使事情难以维护。

这是我想做的——请注意,这可能与实际可能的/最佳实践/无论如何不同——使用 VB.NET、Framework 2.0(所以没有匿名方法):

  Private Sub HandleBtnClick(sender as Object, e as EventArgs) Handles Button.Click
      LoadingScreen.Show()

      'Do stuff here, this takes a while!'
      Dim Result as Object = DoSomethingTakingALongTime(SomeControl.SelectedObject)

      LoadingScreen.Hide()

      ProcessResults(Result)
  End Sub

该应用程序现在完全是单线程的,所以一切都在 GUI 线程上运行。我需要能够在DoSomethingTakingALongTime()没有跨线程异常的情况下访问对象。GUI 线程等待某个方法(这需要很长时间)完成,而LoadingScreen表单应该保持响应(它是动画的/有一个进度条等)。

这是一种可行/好的方法,还是我认为这种方式过于简单?关于这个问题的最佳做法是什么?最重要的是:我怎样才能实现这样的系统?正如我已经提到的,我对线程的经验很少,所以请保持温和:-)

4

3 回答 3

4

您的问题是,当您尝试将 Worker 线程数据传递给您的 ui 线程时,您会遇到跨线程异常。您需要做的是在您的 ui 上设置控件之前检查 InvokeRequired 和 begininvoke,这样您就不会收到如下错误:

Private Sub work_CrossThreadEvent(ByVal sender As Object, ByVal e As System.EventArgs) Handles work.CrossThreadEvent

       If Me.InvokeRequired Then
           Me.BeginInvoke(New EventHandler(AddressOf work_CrossThreadEvent), New Object() {sender, e})
           Return
       End If

      Me.Text = "Cross Thread"

End Sub

只需将New EventHandler部分更改为您使用的事件处理程序。

另外我认为使用后台工作者对你的工作者类来说不是一个坏方法,只需为你的工作创建一个类并使用后台工作者来做线程工作有点像这样:

Public MustInherit Class Worker

    Protected WithEvents worker As BackgroundWorker

    Public Sub New()

        worker = New BackgroundWorker()
        worker.WorkerReportsProgress = True
        worker.WorkerSupportsCancellation = True

    End Sub

    Public Sub Start()

        If (Not worker.IsBusy AndAlso Not worker.CancellationPending) Then
            worker.RunWorkerAsync()
        End If

    End Sub

    Public Sub Cancel()
        If (worker.IsBusy AndAlso Not worker.CancellationPending) Then
            worker.CancelAsync()
        End If
    End Sub

    Protected MustOverride Sub Work()

    Private Sub OnDoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork
        Work()
    End Sub

    Public Event WorkCompelted As RunWorkerCompletedEventHandler
    Private Sub OnRunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
        OnRunWorkerCompleted(e)
    End Sub
    Protected Overridable Sub OnRunWorkerCompleted(ByVal e As RunWorkerCompletedEventArgs)
        RaiseEvent WorkCompelted(Me, e)
    End Sub

    Public Event ProgressChanged As ProgressChangedEventHandler
    Private Sub OnProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles worker.ProgressChanged
        OnProgressChanged(e)
    End Sub
    Protected Overridable Sub OnProgressChanged(ByVal e As ProgressChangedEventArgs)
        RaiseEvent ProgressChanged(Me, e)
    End Sub

End Class

Public Class ActualWork
    Inherits Worker

    Public Event CrossThreadEvent As EventHandler

    Protected Overrides Sub Work()

        'do work here'
        WorkABit()
        worker.ReportProgress(25)

        WorkABit()
        worker.ReportProgress(50)

        WorkABit()
        worker.ReportProgress(75)

        WorkABit()
        worker.ReportProgress(100)

    End Sub

    Private Sub WorkABit()

        If worker.CancellationPending Then Return
        Thread.Sleep(1000)
        RaiseEvent CrossThreadEvent(Me, EventArgs.Empty)

    End Sub

End Class

免责声明.. vb 有点生疏,但你应该明白。

于 2008-12-01T13:21:33.117 回答
0

在您的线程中,使用 Application.Run(yourform) 来获得您想要的。

请注意,您需要以某种方式向表单发出信号以自行关闭。

于 2008-11-25T10:31:02.290 回答
0

我希望你不会觉得这没有帮助 - 但我会质疑为什么你想要一个线程等待屏幕?首先使用线程的原因是为了让 UI 保持响应,并且在后台完成长时间的操作。

否则,您还不如在 FormLoading 控件上有一个 ProgressBar,并让 DoSomethingTakingALongTime 定期更新它。这根本不需要线程。

于 2008-11-25T10:41:11.180 回答