Pregunta

Estoy buscando un método genérico para implementar una pantalla de espera durante operaciones largas. He usado el enhebrado varias veces antes, pero tengo la sensación de que lo implementé muy mal o con demasiadas molestias (y copiar / pegar: ¡el horror!).

Quiero mantener esto lo más genérico y simple posible, por lo que no tendré que implementar un montón de BackgroundWorker s manejando todo tipo de basura, haciendo que las cosas sean difíciles de mantener.

Esto es lo que me gustaría hacer: tenga en cuenta que esto puede diferir de lo que es realmente posible / mejor práctica / lo que sea, usando VB.NET, Framework 2.0 (por lo que no hay métodos anónimos):

  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

La aplicación ahora es completamente de un solo subproceso, por lo que todo se ejecuta en el hilo de la GUI. Necesito poder acceder a objetos en DoSomethingTakingALongTime () sin obtener excepciones de hilos cruzados. El hilo de la GUI espera que se complete algún método (que lleva mucho tiempo), mientras que el formulario LoadingScreen debe responder (está animado / tiene una barra de progreso / etc.).

¿Es este un enfoque factible / bueno o lo veo demasiado simplista? ¿Cuál es la mejor práctica con respecto a este asunto? Y lo más importante: ¿cómo podría implementar tal sistema? Como ya mencioné, tengo muy poca experiencia con el enhebrado, así que sea gentil, por favor :-)

¿Fue útil?

Solución

Su problema es que obtiene una excepción de subproceso cruzado cuando intenta pasar sus datos de subproceso de trabajo a su subproceso de interfaz de usuario. lo que debe hacer es marcar InvokeRequired y comenzar a invocar antes de configurar los controles en su interfaz de usuario para que no reciba el error de esta manera:

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

simplemente cambie la parte New EventHandler al controlador de eventos que está usando.

También creo que usar un trabajador en segundo plano no es un mal método para sus clases de trabajadores, solo cree una clase para su trabajo y use el trabajador de fondo para hacer las cosas de subprocesos de esta manera:

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

descargo de responsabilidad ... un poco oxidado con vb, pero debería tener la idea.

Otros consejos

En su hilo, use Application.Run (yourform) para obtener lo que quiere.

Tenga en cuenta que debe indicarle al formulario que se cierre de alguna manera.

Espero que no le resulte útil, pero le preguntaría ¿POR QUÉ desea una pantalla de espera con hilos? La razón para usar el subproceso en primer lugar es para que la interfaz de usuario permanezca receptiva y se realicen operaciones largas en segundo plano.

De lo contrario, también podría tener una barra de progreso en su control FormLoading y tener DoSomethingTakingALongTime para actualizarlo periódicamente. Esto no necesitaría hilos en absoluto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top