Come posso cedere al thread dell'interfaccia utente per aggiornare l'interfaccia utente mentre eseguo l'elaborazione batch in un'app WinForm?

StackOverflow https://stackoverflow.com/questions/106036

  •  01-07-2019
  •  | 
  •  

Domanda

Ho un'app WinForms scritta in C # con .NET 3.5. Esegue un lungo processo batch. Voglio che l'app aggiorni lo stato di ciò che sta facendo il processo batch. Qual è il modo migliore per aggiornare l'interfaccia utente?

È stato utile?

Soluzione

BackgroundWorker suona come l'oggetto desiderato.

Altri suggerimenti

Il modo più veloce e sporco è usare Application.DoEvents () ma questo può causare problemi con la gestione degli eventi dell'ordine. Quindi non è raccomandato

Il problema probabilmente non è che devi cedere al thread dell'interfaccia utente ma che esegui l'elaborazione sul thread dell'interfaccia utente bloccandolo dalla gestione dei messaggi. Puoi utilizzare il componente backgroundworker per eseguire l'elaborazione batch su un thread diverso senza bloccare il thread dell'interfaccia utente.

Esegui il lungo processo su un thread in background. La classe lavoratore in background è un modo semplice per farlo: fornisce un semplice supporto per l'invio di aggiornamenti di avanzamento e eventi di completamento per i quali i gestori di eventi vengono chiamati sul thread corretto per te. Ciò mantiene il codice pulito e conciso.

Per visualizzare gli aggiornamenti, le barre di avanzamento o il testo della barra di stato sono due degli approcci più comuni.

La cosa fondamentale da ricordare è che se stai facendo cose su un thread in background, devi passare al thread dell'interfaccia utente per aggiornare i controlli di Windows ecc.

Per rinforzare ciò che la gente dice su DoEvents, ecco una descrizione di ciò che può accadere.

Supponi di avere un modulo con dati su di esso e il tuo evento di lunga durata lo sta salvando nel database o sta generando un rapporto basato su di esso. Inizi a salvare o generare il rapporto, quindi periodicamente chiami DoEvents in modo che lo schermo continui a dipingere.

Sfortunatamente lo schermo non sta solo dipingendo, ma reagirà anche alle azioni dell'utente. Questo perché DoEvents interrompe ciò che stai facendo ora per elaborare tutti i messaggi di Windows in attesa di essere elaborati dalla tua app Winforms. Questi messaggi includono richieste di ridisegno, nonché qualsiasi tipo di utente, clic, ecc.

Ad esempio, mentre stai salvando i dati, l'utente può fare cose come fare in modo che l'app mostri una finestra di dialogo modale completamente estranea all'attività di lunga durata (ad es. Aiuto- > Informazioni). Ora stai reagendo alle nuove azioni dell'utente all'interno dell'attività già in esecuzione da molto tempo. DoEvents ritornerà quando tutti gli eventi che erano in attesa quando l'hai chiamato sono terminati, e quindi l'attività di lunga durata continuerà.

Cosa succede se l'utente non chiude la finestra di dialogo modale? L'attività di lunga durata attende per sempre fino alla chiusura di questa finestra di dialogo. Se ti stai impegnando in un database e stai trattenendo una transazione, ora stai tenendo aperta una transazione mentre l'utente sta bevendo un caffè. O la transazione scade e perdi il lavoro di persistenza, oppure la transazione non scade e potresti potenzialmente bloccare gli altri utenti del DB.

Quello che sta succedendo qui è che Application.DoEvents rende il tuo codice rientrante. Consulta la definizione di Wikipedia qui . Nota alcuni punti della parte superiore dell'articolo, che per far rientrare il codice:

  • Non deve contenere dati non costanti statici (o globali).
  • Deve funzionare solo sui dati forniti dal chiamante.
  • Non deve fare affidamento sui blocchi alle risorse singleton.
  • Non è necessario chiamare programmi o routine per computer non rientranti.

È molto improbabile che il codice a esecuzione prolungata in un'app WinForms funzioni solo sui dati passati al metodo dal chiamante, non contiene dati statici, non contiene blocchi e chiama solo altri metodi rientranti.

Come molte persone qui dicono, DoEvents può portare ad alcuni scenari molto strani nel codice. I bug che può causare possono essere molto difficili da diagnosticare e il tuo utente non è in grado di dirti " Oh, questo potrebbe essere successo perché ho fatto clic su questo pulsante non correlato mentre aspettavo che salvasse " ;.

Usa il componente backgroundworker per eseguire l'elaborazione batch in un thread separato, questo non avrà alcun impatto sul thread dell'interfaccia utente.

Voglio riaffermare ciò che i miei precedenti commentatori hanno notato: per favore evita DoEvents () ogni volta che è possibile, poiché questa è quasi sempre una forma di "quot" hack " e provoca incubi di manutenzione.

Se segui la strada BackgroundWorker (che ti suggerisco), dovrai gestire le chiamate cross-threading all'interfaccia utente se desideri chiamare metodi o proprietà dei controlli, poiché sono affini al thread e devono essere chiamato solo dal thread su cui sono stati creati. Utilizzare Control.Invoke () e / o Control.BeginInvoke () come appropriato.

Se si esegue un thread in background / worker, è possibile chiamare Control.Invoke su uno dei controlli dell'interfaccia utente per eseguire un delegato nel thread dell'interfaccia utente.

Control.Invoke è sincrono (attende fino a quando il delegato non ritorna). Se non vuoi aspettare, usa .BeginInvoke () per mettere in coda solo il comando.

Il valore di ritorno di .BeginInvoke () ti consente di verificare se il metodo è stato completato o di attendere fino al suo completamento.

Usa Backgroundworker e se stai anche provando ad aggiornare il thread della GUI gestendo l'evento ProgressChanged (come, per un ProgressBar ) , assicurati di impostare anche WorkerReportsProgress = true , altrimenti il ??thread che segnala lo stato di avanzamento morirà la prima volta che tenta di chiamare ReportProgress ...

viene generata un'eccezione, ma potresti non vederla a meno che tu non abbia abilitato 'quando lanciato' e l'output mostrerà solo che il thread è uscito.

Application.DoEvents () o eventualmente eseguire il batch su un thread separato?

DoEvents () era quello che stavo cercando, ma ho anche votato le risposte del lavoratore in background perché sembra una buona soluzione che approfondirò ancora.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top