5

Ich ändere ein Windows-Formular, damit Daten im Hintergrund geladen werden können, während die Benutzeroberfläche reaktionsfähig bleibt. Die Daten benötigen spürbare Zeit sowohl zum Abrufen als auch zum Binden. Im Idealfall würde ich beides im Hintergrund tun, aber es gibt einige Unklarheiten darüber, welche Art von UI-Updates ich im Hintergrund machen soll (wie außerhalb des Hauptthreads). Ein solides Beispiel, das Datenabruf und Datenbindung im Hintergrund zeigt, wäre sehr hilfreich.Reaktion der WinForms-Benutzeroberfläche beim Umgang mit "schweren" Daten

Antwort

6

Der Abruf kann und soll, werden zu einem Hintergrundthread abgeschoben - aber es gibt einige Muster alles an Ort und Stelle zu setzen.

Im Grunde werden Sie einen Hintergrund-Thread zum Abrufen der Daten starten, sobald es fertig ist, muss es wieder in den UI-Thread zusammengeführt werden, um die tatsächlichen UI-Updates durchzuführen (UI-Updates über Threads sind schlecht schlecht schlecht).

ist es drei grundlegende Arten von Hintergrund Threading für Sie

  • die einfachste/meist begrenzt (und schrullige IMO) zu erkunden, sind die Background Komponente
  • mit Delegierten und deren BeginInvoke()/EndInvoke() -Methoden bieten eine gute Balance von Leichtigkeit und Flexibilität (und Threadpool Threads verwenden)
  • mit Rohfaden Objekte bietet die Kontrolle, sind aber langsamer zu Setup als Threadpool Threads

Persönlich lehne ich mich der Delegierten-Option zu; Sie sind ziemlich einfach zu bearbeiten, sobald Sie das Muster ablegen. Der BackgroundWorker scheint vorne nett zu sein, hat aber einige Probleme und fehlende Installationen, die es mühsamer machen, mit der Arbeit zu beginnen, als Sie erwarten würden. Lassen Sie mich eine kurze Probe des Delegate-Ansatzes machen; Ich werde in Kürze aktualisieren ...

bearbeiten

Hier ist ein Code, dann ist es in VB aber sollte leicht genug sein, zu transkribieren, wenn Sie ein C# Kerl sind. Sie haben noch ein paar Optionen, wie sich der Hintergrund-Thread verhalten soll, also gibt es hier zwei Beispiele. Non-Blocking ist mein Favorit, aber wenn Sie es in vorhandenen Code anpassen, dann könnte die Blockierung für Sie einfacher sein.

Non-blocking, die Callback-Methode (GetData_Complete) auf dem UI-Thread aufgerufen werden, wenn der Hintergrund-Thread abgeschlossen ist

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 

Blocking 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

Aktualisieren Sie die Benutzeroberfläche niemals von einem Hintergrundthread. Nachdem Sie Ihre Daten vom Server abgerufen haben, rufen Sie den UI-Thread erneut auf, um die UI-Steuerelemente oder das Dataset zu aktualisieren, an die Ihre UI gebunden ist.

Die Verwendung von BackgroundWorker hilft in diesem Fall nur die Ereignisse zu verkabeln.

HTH

Phil‘

+0

Das war meine Hauptsorge. Ich war mir ziemlich sicher, dass ich keine Dinge wie control.Datasource = myData; im Hintergrund, aber es dauert einige Zeit zu vervollständigen. Daher war ich vor allem neugierig auf das Ausmaß der Arbeit, die ich rechtfertigen konnte, in den Hintergrund zu stellen. – ramnik

0

Das Laden (wie beim "Abrufen aus der Datenquelle") kann trivial sein, egal ob Sie Delegaten, Hintergrundarbeiter oder ein anderes Protokoll verwenden.Die Bindung scheint jedoch schwierig, da es kaum Kontrolle gibt, zumindest in den meisten datengebundenen Steuerelementen. Sie können Daten asynchron abrufen, aber sobald Sie sie bereit haben, können Sie sie im Hintergrund in ein großes Raster einspeisen. Ist das deine Frage? Wenn ja, können Sie entweder:

  • erstellen (oder Unterklasse) Ihre Ansicht Steuerelement, die Schnittstellen für asynchrone Last;
  • implementieren Sie eine seitenweise Ansicht, die nur N Datensätze gleichzeitig zeigt, so dass die Benutzeroberfläche beim Abrufen/Formatieren von Datensätzen nicht blockiert wird.