5

Sto modificando un modulo di Windows per consentire il caricamento dei dati in background mentre l'interfaccia utente rimane reattiva. I dati richiedono tempi notevoli per il recupero e il binding. Idealmente, farei entrambe le cose in background, ma c'è qualche ambiguità su quale tipo di aggiornamenti UI dovrei fare in background (come al di fuori del thread principale). Un solido esempio che mostra il recupero dei dati e l'associazione dei dati in background sarebbe molto utile.WinForms reattività dell'interfaccia utente quando si tratta di dati "pesanti"

risposta

6

Il recupero può, e deve, essere spinto fuori ad un thread in background - ma c'è alcuni modelli a mettere tutto a posto.

Fondamentalmente si avvia un thread in background per recuperare i dati, una volta terminato, sarà necessario unire nuovamente il thread dell'interfaccia utente per eseguire gli aggiornamenti dell'interfaccia utente effettivi (gli aggiornamenti dell'interfaccia tra thread sono negativi).

Ci sono tre modi di base di sfondo filettatura per esplorare

  • il più facile/più limitata (e stravagante IMO) è la componente BackgroundWorker
  • utilizzando i delegati e) Metodi di loro BeginInvoke()/EndInvoke (fornire un buon equilibrio di facilità e flessibilità (e utilizzare ThreadPool discussioni)
  • utilizzando oggetti della discussione prime offre il massimo controllo, ma sono più lenti per l'installazione di ThreadPool discussioni

Personalmente mi chino verso l'opzione Delegati; sono abbastanza facili da lavorare quando si ottiene il modello. The BackgroundWorker sembra bello in primo piano, ma ha alcuni trucchi e tubature mancanti che rendono più complicato lavorare con quanto ci si aspetterebbe. Lasciatemi fare un breve esempio dell'approccio Delegato; Io aggiornerò prossimamente ...

modificare

Ecco un po 'di codice, è in VB, ma dovrebbe essere abbastanza facile da trascrivere, se sei un C# ragazzo. Hai un paio di altre opzioni su come vuoi che si comporti il ​​thread in background, quindi qui ci sono due esempi. Il non blocco è il mio preferito, ma se lo stai inserendo nel codice esistente, il blocco potrebbe essere più facile per te.

non bloccante, il metodo di callback (GetData_Complete) sarà chiamato sul thread UI una volta che il thread in background è 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 

Blocco 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

Non aggiornare mai l'interfaccia utente da alcun thread in background, una volta recuperati i dati dal server richiamare il thread dell'interfaccia utente per aggiornare i controlli dell'interfaccia utente o il set di dati a cui è collegata l'interfaccia utente.

Utilizzando BackgroundWorker sarà utile in questo caso collegare solo gli eventi.

HTH

Phil'

+0

Questa era la mia preoccupazione principale. Ero abbastanza sicuro che non dovevo fare cose come control.Datasource = myData; sullo sfondo, ma ci vuole del tempo per completare. Quindi ero per lo più curioso dell'estensione del lavoro che potrei giustificare mettendo in secondo piano. – ramnik

0

di carico (come in "recupero dall'origine dati") potrebbe essere banale, se si utilizzano i delegati, i lavoratori di sfondo o qualsiasi altro protocollo.Ma il binding sembra complicato, perché non c'è molto controllo su cui può esercitarsi, almeno nella maggior parte dei controlli con associazione a dati: è possibile recuperare i dati in modo asincrono, ma una volta pronto come alimentarlo a una grande griglia in background? È questa la tua domanda? In tal caso, è possibile che:

  • creare (o sottoclasse) il controllo della vista, fornendo interfacce per il carico asincrono;
  • implementa una visualizzazione a pagine, mostrando solo N record alla volta, in modo che l'interfaccia utente non sia bloccata durante il recupero/la formattazione dei record.