2009-10-01 15 views
142

Ho una domanda stilistica sulla scelta dell'implementazione del thread in background che dovrei usare su un'app per Windows. Attualmente ho un BackgroundWorker su un modulo che ha un ciclo infinito (while(true)). In questo ciclo uso WaitHandle.WaitAny per mantenere il thread che si sonnecchia fino a quando non si verifica qualcosa di interessante. Uno degli handle di evento che aspetto è un evento "StopThread" in modo che possa uscire dal ciclo. Questo evento viene segnalato quando dal mio override Form.Dispose().BackgroundWorker vs background Thread

Ho letto da qualche parte che lo standard BackgroundWorker è destinato alle operazioni che non si desidera legare all'interfaccia utente con una fine finita - come scaricare un file o elaborare una sequenza di elementi. In questo caso la "fine" è sconosciuta e solo quando la finestra è chiusa. Quindi sarebbe più appropriato per me utilizzare una discussione in background invece di BackgroundWorker per questo scopo?

risposta

72

Dalla mia comprensione della domanda, si sta utilizzando un BackgroundWorker come un thread standard.

Il motivo per cui BackgroundWorker è consigliato per cose che non si desidera legare il thread dell'interfaccia utente è perché espone alcuni eventi piacevoli quando si esegue lo sviluppo di Win Form.

Eventi come RunWorkerCompleted per segnalare quando il thread ha completato ciò che era necessario fare e l'evento ProgressChanged per aggiornare la GUI sull'avanzamento dei thread.

Quindi se si non è facendo uso di questi, non vedo alcun danno nell'usare un thread standard per ciò che è necessario fare.

+0

un altro problema di cui sono sicuro, supponiamo che sto cercando di disporre del modulo su cui è in esecuzione il Backgroundworker. Segnalo lo shutdownevent (ManualResetEvent) e, qualche volta dopo, DoWork uscirà con grazia.Dovrei semplicemente lasciare che il modulo vada avanti e Dispose anche se il DoWork potrebbe richiedere un po 'più di tempo per finire, o c'è un modo (ed è meglio) per fare il thread. Entrare nel worker in background fino a quando non esce e poi lasciare il Dispose del modulo continua? –

+0

Penso che BackgroundWorker.IsBusy sia quello che stai cercando lì. – ParmesanCodice

+1

Usa solo 'CancelAsync' (e prova per' CancellationPending' se il tuo thread eseguirà il polling su brevi intervalli, se invece vuoi che venga sollevata un'eccezione, usa un 'System.Threading.Thread.Abort()' che solleva un'eccezione all'interno del thread block stesso, scegli il modello giusto per la situazione –

2

Se non è rotto - risolvere il problema fino a che è ... solo scherzando :)

Ma sul serio BackgroundWorker è probabilmente molto simile a quello che già avete, se aveste iniziato con essa fin dall'inizio forse si avrei risparmiato un po 'di tempo - ma a questo punto non vedo il bisogno. A meno che qualcosa non funzioni, o pensi che il tuo codice attuale sia difficile da capire, allora resterei fedele a ciò che hai.

1

Un worker in background è una classe che funziona in un thread separato, ma fornisce funzionalità aggiuntive che non si ottengono con un thread semplice (come la gestione del report di avanzamento dell'attività).

Se non hai bisogno delle funzionalità aggiuntive fornite da un addetto al background - e sembra che tu non lo faccia - allora un Thread sarebbe più appropriato.

2

La differenza di base è, come hai affermato, la generazione di eventi GUI da BackgroundWorker. Se il thread non ha bisogno di aggiornare il display o generare eventi per il thread della GUI principale, allora può essere un thread semplice.

10

Inoltre si sta vincolando un thread del threadpool per tutta la durata del worker in background, il che può essere motivo di preoccupazione in quanto ne esiste solo un numero limitato. Direi che se crei il thread una sola volta per la tua app (e non utilizzi nessuna delle funzionalità di background worker), allora usi un thread, piuttosto che un thread backgroundworker/threadpool.

+1

Penso che questo sia un buon punto, quindi il messaggio che prendo da questo è usare un background worker se hai bisogno di un thread in background "temporaneamente" durante la vita del Form tuttavia, se è necessario un thread in background per l'intera durata del modulo (che potrebbe essere minuti, ore, giorni ...), utilizzare una discussione anziché BackgroundWorker in modo da non utilizzare in modo errato lo scopo del ThreadPool –

+0

Informazioni su: ". ..che può essere motivo di preoccupazione in quanto ne esiste solo un numero finito ", vuoi dire che altre app sul sistema operativo potrebbero averne bisogno e condividerle dallo stesso" pool "? –

327

Alcuni dei miei pensieri ...

  1. Usa BackgroundWorker se si dispone di un unico compito che viene eseguito in background e ha bisogno di interagire con l'interfaccia utente. L'attività di marshalling di dati e chiamate di metodi al thread dell'interfaccia utente viene gestita automaticamente tramite il relativo modello basato su eventi. Evita BackgroundWorker se ...
    • vostra assemblea non ha o non interagisce direttamente con l'interfaccia utente,
    • è necessario il filo ad essere un filo di primo piano, o
    • è necessario manipolare la priorità del thread.
  2. Utilizzare una filettatura ThreadPool quando si desidera l'efficienza. ThreadPool aiuta a evitare l'overhead associato alla creazione, all'avvio e all'arresto dei thread. Evitare di utilizzare il ThreadPool se ...
    • l'attività viene eseguita per tutta la durata della vostra applicazione,
    • è necessario il filo ad essere un filo di primo piano,
    • è necessario manipolare la priorità del thread, o
    • è necessario che il thread abbia un'identità fissa (interruzione, sospensione, rilevamento).
  3. utilizzare la classe Thread per le attività di lunga durata e quando si richiedono funzionalità offerte da un modello di threading formali, ad esempio, la scelta tra il primo piano e sfondo thread, modificando la priorità del thread, a grana fine il controllo sulla esecuzione del thread, ecc .
+9

Background worker è nel system.dll ass embly e System.ComponentModel namespace. Non c'è dipendenza da Winforms. – Kugel

+14

Ciò è corretto, ma 'BackgroundWorker' è progettato per riportare l'avanzamento del thread a una parte interessata, che di solito implica un'interfaccia utente. La documentazione MSDN per la classe lo rende abbondantemente chiaro. Se hai semplicemente bisogno di un compito da eseguire in background, preferisci usare un thread 'ThreadPool'. –

+5

Riguardo al vostro punto sull'assembly 'System.Windows.Forms'; 'BackgroundWorker' è utile anche per le app WPF e quelle app potrebbero non avere un riferimento a WinForms. – GiddyUpHorsey

11

più o meno quello che Matt Davis ha detto, con i seguenti punti aggiuntivi:

Per me la differenziazione principale con BackgroundWorker è lo smistamento automatico della manifestazione completata attraverso il SynchronizationContext. In un contesto di interfaccia utente, ciò significa che l'evento completato viene attivato nel thread dell'interfaccia utente e può quindi essere utilizzato per aggiornare l'interfaccia utente. Questo è un importante elemento di differenziazione se si utilizza lo BackgroundWorker in un contesto di interfaccia utente.

Le attività eseguite tramite ThreadPool non possono essere annullate facilmente (questo include ThreadPool. QueueUserWorkItem ei delegati vengono eseguiti in modo asincrono). Quindi, mentre evita il sovraccarico di thread spinup, se hai bisogno di cancellare usa uno BackgroundWorker o (più probabilmente al di fuori dell'interfaccia utente) fai girare un thread e tieni un riferimento ad esso in modo da poter chiamare Abort().

+1

Solo ... si spera che l'applicazione sia progettata per un metodo * clean * di arresto di un'attività filettata (in genere non è Abort) –

4

Sapevo come usare i thread prima che io conoscessi .NET, quindi ci ho messo un po 'ad abituarmi a quando ho iniziato ad usare BackgroundWorkers. Matt Davis ha riassunto la differenza con grande eccellenza, ma vorrei aggiungere che è più difficile capire esattamente cosa sta facendo il codice, e questo può rendere più difficile il debugging. È più facile pensare a creare e chiudere i thread, IMO, piuttosto che pensare di dare lavoro a un pool di thread.

Io ancora non posso commentare i post di altre persone, in modo da perdona la mia momentanea zoppia nel usando una risposta per affrontare piers7

Non utilizzare Thread.Abort(); invece, segnala un evento e progetta il tuo thread per terminare con garbo quando segnalato. Thread.Abort() genera una ThreadAbortException in un punto arbitrario nell'esecuzione del thread, che può eseguire tutti i tipi di cose infelici come Monitors orfani, stato condiviso corrotto e così via. http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx

8

Sapete, a volte è solo più semplice lavorare con un BackgroundWorker, indipendentemente dal fatto che si utilizzi Windows Form, WPF o qualsiasi altra tecnologia. La parte interessante di questi ragazzi è che si ottiene il threading senza doversi preoccupare troppo di dove si sta eseguendo il thread, il che è ottimo per le attività semplici.

Prima di utilizzare un BackgroundWorker, considerare innanzitutto se si desidera annullare un thread (chiusura dell'app, annullamento dell'utente), quindi è necessario decidere se il thread deve controllare le cancellazioni o se deve essere indirizzato all'esecuzione stessa.

BackgroundWorker.CancelAsync() imposterà CancellationPending a true ma non farà nulla di più, è allora la responsabilità thread per controllare continuamente questo, tenere presente anche che si potrebbe finire con una condizione di competizione in questo approccio in cui il vostro utente ha annullato, ma il thread completato prima del test per CancellationPending.

Thread.Abort() d'altra parte genererà un'eccezione nell'esecuzione del thread che impone l'annullamento di quel thread, è necessario prestare attenzione a ciò che potrebbe essere pericoloso se questa eccezione è stata sollevata improvvisamente all'interno dell'esecuzione però.

Threading ha bisogno di molto attenta considerazione non importa quale sia il compito, per qualche ulteriore lettura:

Parallel Programming in the .NET Framework Managed Threading Best Practices

0

Voglio sottolineare un comportamento della classe BackgroundWorker che non è stato ancora citato. È possibile eseguire un normale thread per l'esecuzione in background impostando la proprietà Thread.IsBackground.

I thread in background sono identici ai thread in primo piano, tranne che i thread in background non impediscono la chiusura di un processo. [1]

È possibile testare questo comportamento chiamando il metodo seguente nel costruttore della finestra del modulo.

void TestBackgroundThread() 
{ 
    var thread = new Thread((ThreadStart)delegate() 
    { 
     long count = 0; 
     while (true) 
     { 
      count++; 
      Debug.WriteLine("Thread loop count: " + count); 
     } 
    }); 

    // Choose one option: 
    thread.IsBackground = false; // <--- This will make the thread run in background 
    thread.IsBackground = true; // <--- This will delay program termination 

    thread.Start(); 
} 

Quando la proprietà IsBackground è impostata su true e si chiude la finestra, quindi l'applicazione terminerà normalmente.

Ma quando la proprietà IsBackground è impostata su false (per impostazione predefinita) e si chiude la finestra, solo la finestra scomparirà ma il processo continuerà a essere in esecuzione.

La classe BackgroundWorker utilizza un thread eseguito in background.

0

Ciò che lascia perplessi è che il designer dello studio visivo consente solo di utilizzare BackgroundWorkers e Timer che non funzionano effettivamente con il progetto di servizio.

Offre controlli di trascinamento e rilascio chiari sul servizio ma ... non provare nemmeno a distribuirlo. Non funzionerà.

Servizi: Solo utilizzare System.Timers.Timer System.Windows.Forms.Timer non funziona anche se è disponibile nella casella degli strumenti

Servizi: BackgroundWorkers non funziona quando è in esecuzione come servizio Utilizzare System.Threading.ThreadPools invece o chiamate asincrone