5

Estoy modificando un formulario de Windows para permitir que los datos se carguen en segundo plano mientras la UI sigue siendo receptiva. Los datos toman un tiempo notable tanto para recuperar como para enlazar. Idealmente, haría ambas cosas en segundo plano, pero hay cierta ambigüedad sobre qué tipo de actualizaciones de UI debería estar haciendo en segundo plano (como fuera del hilo principal). Un ejemplo sólido que muestre la recuperación de datos y el enlace de datos en el fondo sería muy útil.WinForms UI capacidad de respuesta cuando se trata de datos "pesados"

Respuesta

6

La recuperación puede, y debe, ser empujado hacia fuera a un subproceso de fondo - pero hay algunas pautas para poner todo en su lugar.

Básicamente, comenzará un hilo de fondo para recuperar los datos, una vez hecho, deberá fusionarse de nuevo en el hilo de la interfaz de usuario para realizar las actualizaciones de UI (las actualizaciones de UI en los hilos son malas).

Hay tres formas básicas de fondo roscado para explorar

  • el más fácil/más limitada (y peculiar OMI) es el componente BackgroundWorker
  • utilizando los delegados y sus BeginInvoke()/EndInvoke() métodos proporcionar un buen equilibrio de facilidad y flexibilidad (y utilizar ThreadPool Hilos)
  • usando proporciona el máximo control de rosca objetos primas, pero son más lentos de instalar que ThreadPool Hilos

Personalmente me inclino hacia la opción Delegados; son bastante fáciles de trabajar una vez que obtienes el patrón. El BackgroundWorker parece agradable desde el principio, pero tiene algunas trampas y falta de plomería que hacen que trabajar sea más complicado de lo que cabría esperar. Permítanme preparar una pequeña muestra del enfoque Delegado; Voy a actualizar en breve ...

edición

Aquí hay un código, que es en VB, pero debe ser lo suficientemente fácil para transcribir si eres un chico de C#. Tienes un par de opciones más sobre cómo quieres que se comporte el hilo de fondo, así que aquí hay dos ejemplos. Mi preferido es el no bloqueo, pero si lo estás adaptando al código existente, entonces el bloqueo podría ser más fácil para ti.

sin bloqueo, el método de devolución de llamada (GetData_Complete) será llamada en el subproceso de interfaz de usuario una vez que el hilo de fondo es completa

Sub Main() 

    Console.WriteLine("On the main thread") 
    Dim dataDelegate As New GetDataCaller(AddressOf GetData) 

    Dim iar As IAsyncResult 

    ' Non-blocking approach using a callback method 
    iar = dataDelegate.BeginInvoke(AddressOf GetData_Complete, Nothing) 

End Sub 

Private Delegate Sub GetData_CompleteCaller(ByVal iar As IAsyncResult) 
Private Sub GetData_Complete(ByVal iar As IAsyncResult) 
    If InvokeRequired Then 
     Dim invokeDelegate As New GetData_CompleteCaller(AddressOf GetData_Complete) 
     Invoke(invokeDelegate, New Object() {iar}) 
     Exit Sub 
    End If 

    ' Downcast the IAsyncResult to an AsyncResult -- it's safe and provides extra methods 
    Dim ar As System.Runtime.Remoting.Messaging.AsyncResult = DirectCast(iar, System.Runtime.Remoting.Messaging.AsyncResult) 

    Dim dataDelegate As GetDataCaller = DirectCast(ar.AsyncDelegate, GetDataCaller) 
    Dim result As String = dataDelegate.EndInvoke(iar) 

    Console.WriteLine("On the main thread again, background result is: " + result) 

End Sub 

Private Delegate Function GetDataCaller() As String 
Private Function GetData() As String 
    Console.WriteLine("On the background thread!") 

    For index As Integer = 0 To 2 
     Console.WriteLine("Background thread is working") 
    Next 

    Return "Yay, background thread got the data!" 

End Function 

bloqueo Sub Main()

Console.WriteLine("On the main thread") 
    Dim dataDelegate As New GetDataCaller(AddressOf GetData) 

    Dim iar As IAsyncResult 

    ' blocking approach; WaitOne() will block this thread from proceeding until the background thread is finished 
    iar = dataDelegate.BeginInvoke(Nothing, Nothing) 
    iar.AsyncWaitHandle.WaitOne() 
    Dim result As String = dataDelegate.EndInvoke(iar) 
    Console.WriteLine("On the main thread again, background result is: " + result) 

End Sub 

Private Sub GetData_Complete(ByVal iar As IAsyncResult) 

    ' Downcast the IAsyncResult to an AsyncResult -- it's safe and provides extra methods 
    Dim ar As System.Runtime.Remoting.Messaging.AsyncResult = DirectCast(iar, System.Runtime.Remoting.Messaging.AsyncResult) 

    Dim dataDelegate As GetDataCaller = DirectCast(ar.AsyncDelegate, GetDataCaller) 
    Dim result As String = dataDelegate.EndInvoke(iar) 

    Console.WriteLine("On the main thread again, background result is: " + result) 

End Sub 

Private Delegate Function GetDataCaller() As String 
Private Function GetData() As String 
    Console.WriteLine("On the background thread!") 

    For index As Integer = 0 To 2 
     Console.WriteLine("Background thread is working") 
    Next 

    Return "Yay, background thread got the data!" 

End Function 
1

No actualice la interfaz de usuario desde ninguna cadena de fondo, una vez que haya recuperado los datos de la invocación de su servidor al hilo de la interfaz de usuario para actualizar los controles de la UI o el conjunto de datos al que está sujeta su interfaz de usuario.

El uso de BackgroundWorker le ayudará en este caso a conectar los eventos.

HTH

Phil'

+0

Esta fue mi principal preocupación. Estaba bastante seguro de que no debería estar haciendo cosas como control.Datasource = myData; en segundo plano, pero lleva tiempo completarlo. Así que tenía curiosidad sobre el alcance del trabajo que podía justificar poner en segundo plano. – ramnik

0

La carga (como en "recuperación desde el origen de datos") puede ser trivial, ya sea que use delegados, trabajadores en segundo plano o cualquier otro protocolo.Pero el enlace parece complicado, porque no hay mucho control que uno pueda ejercer sobre él, al menos en la mayoría de los controles vinculados a datos: puede recuperar datos de forma asincrónica, pero una vez que lo tiene listo ¿cómo alimentarlo a una grilla grande en el fondo? ¿Esa es tu pregunta? Si es así, creo que puede:

  • crear (o subclase) su control de vista, que proporciona interfaces para la carga asíncrona;
  • implementa una vista paginada, que muestra solo N registros a la vez, para que la IU no se bloquee mientras se recuperan/formatean registros.
Cuestiones relacionadas