Discussione di BackgroundWorker vs background


166

Ho una domanda stilistica sulla scelta dell'implementazione del thread in background che dovrei usare su un'app di Windows Form. Attualmente ho BackgroundWorkerun modulo su che ha un (while(true))ciclo infinito . In questo ciclo uso WaitHandle.WaitAnyper mantenere il thread snoozing fino a quando accade qualcosa di interessante. Uno degli handle dell'evento che aspetto è un " StopThread" evento in modo da poter uscire dal ciclo. Questo evento viene segnalato quando dal mio override Form.Dispose().

Ho letto da qualche parte che BackgroundWorkerè veramente destinato a operazioni con le quali non si desidera legare l'interfaccia utente e avere 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. Pertanto sarebbe più appropriato per me utilizzare un thread in background anziché BackgroundWorkerper questo scopo?

Risposte:


88

Dalla mia comprensione della tua domanda, stai usando un BackgroundWorkerthread standard.

Il motivo per cui BackgroundWorkerè raccomandato per cose che non si desidera legare il thread dell'interfaccia utente è perché espone alcuni eventi interessanti durante lo sviluppo di Win Forms.

A eventi piace RunWorkerCompletedsegnalare quando il thread ha completato ciò che doveva fare e l' ProgressChangedevento per aggiornare la GUI sull'avanzamento del thread.

Quindi, se non li usi, non vedo alcun danno nell'usare un thread standard per quello che devi fare.


un altro problema di cui non sono sicuro è, supponiamo che stia cercando di eliminare il modulo su cui è in esecuzione il lavoratore in background. Segnalo lo shutdownevent (ManualResetEvent) e qualche volta dopo DoWork uscirà con grazia. Devo solo lasciare che il modulo vada avanti e Dispose anche se DoWork potrebbe richiedere un po 'più di tempo per terminare, o c'è un modo (ed è meglio) di thread. Unire il lavoratore in background fino a quando non esce davvero e quindi lasciare che Dispose del modulo continua?
freddy smith,

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

1
Usa solo CancelAsync(e verifica CancellationPendingse il tuo thread eseguirà il polling su brevi intervalli, se vuoi invece generare un'eccezione, usa un System.Threading.Thread.Abort()che genera un'eccezione all'interno del blocco thread stesso, scegli il modello giusto per la situazione.
Brett Ryan

369

Alcuni dei miei pensieri ...

  1. Usa BackgroundWorker se hai un'unica attività che viene eseguita in background e deve interagire con l'interfaccia utente. L'attività di marshalling dei dati e delle chiamate di metodo al thread dell'interfaccia utente viene gestita automaticamente attraverso il suo modello basato su eventi. Evita BackgroundWorker se ...
    • l'assembly non ha o non interagisce direttamente con l'interfaccia utente,
    • è necessario che il thread sia un thread in primo piano o
    • devi manipolare la priorità del thread.
  2. Utilizzare un thread ThreadPool quando si desidera l'efficienza. ThreadPool aiuta a evitare il sovraccarico associato alla creazione, all'avvio e all'arresto dei thread. Evita di usare ThreadPool se ...
    • l'attività viene eseguita per tutta la durata dell'applicazione,
    • hai bisogno che il thread sia un thread in primo piano,
    • devi manipolare la priorità del thread, o
    • è necessario che il thread abbia un'identità fissa (interruzione, sospensione, scoperta).
  3. Utilizzare la classe Thread per attività di lunga durata e quando sono richieste funzionalità offerte da un modello di threading formale, ad esempio, la scelta tra thread in primo piano e di sfondo, l'ottimizzazione della priorità del thread, il controllo dettagliato sull'esecuzione del thread, ecc.

10
Il lavoratore in background si trova nell'assieme System.dll e nello spazio dei nomi System.ComponentModel. Non c'è dipendenza da Winforms.
Kugel,

17
È corretto, ma BackgroundWorkerè progettato per segnalare l'avanzamento del thread a una parte interessata, che di solito comporta un'interfaccia utente. La documentazione MSDN per la classe lo rende abbondantemente chiaro. Se hai semplicemente bisogno di eseguire un'attività in background, preferisci usare un ThreadPoolthread.
Matt Davis,

5
Per quanto riguarda il tuo punto System.Windows.Formssull'assemblea; BackgroundWorkerè utile anche per le app WPF e tali app potrebbero non avere un riferimento a WinForms.
GiddyUpHorsey,

12

Praticamente quello che ha detto Matt Davis, con i seguenti punti aggiuntivi:

Per me il principale BackgroundWorkerfattore di differenziazione è il marshalling automatico dell'evento completato tramite il SynchronizationContext. In un contesto di UI questo significa che l'evento completato viene generato sul thread dell'interfaccia utente e quindi può essere utilizzato per aggiornare l'interfaccia utente. Questo è un grosso fattore di differenziazione se si utilizza il BackgroundWorkerin un contesto UI.

Le attività eseguite tramite il ThreadPoolnon possono essere facilmente annullate (questo include ThreadPool. QueueUserWorkItemE i delegati eseguono in modo asincrono). Quindi, mentre evita il sovraccarico dello spinup del thread, se hai bisogno di annullamento usa una BackgroundWorkero (più probabilmente al di fuori dell'interfaccia utente) gira un thread e mantieni un riferimento per poterlo chiamare Abort().


1
Solo ... si spera che l'applicazione sia progettata su un metodo pulito per interrompere un'attività con thread (Abort in genere non è vero)

11

Inoltre stai legando un thread di threadpool per la vita del lavoratore in background, il che potrebbe essere fonte di preoccupazione in quanto ne esiste solo un numero finito. Direi che se crei il thread una sola volta per la tua app (e non usi nessuna delle funzionalità di background worker), usa un thread, piuttosto che un thread backgroundworker / threadpool.


1
Penso che questo sia un buon punto. Quindi il messaggio che prendo da questo è utilizzare un lavoratore in background se hai bisogno di un thread in background "temporaneamente" durante la durata del modulo, tuttavia se hai bisogno di un thread in background per l'intera durata del modulo (che potrebbe essere minuti, ore, giorni ...) quindi usa un thread invece di BackgroundWorker in modo da non abusare dello scopo di ThreadPool
freddy smith

Per quanto riguarda: "... che può essere preoccupante dato che ce ne sono solo un numero finito", vuoi dire che altre app sul sistema operativo potrebbero averne bisogno e condividerle dallo stesso "pool"?
Dan W,

8

Sai, a volte è più semplice lavorare con un BackgroundWorker indipendentemente dal fatto che tu stia utilizzando Windows Form, WPF o qualunque tecnologia. La parte bella di questi ragazzi è che stai iniziando il thread senza preoccuparti troppo di dove stai eseguendo il thread, il che è ottimo per compiti semplici.

Prima di utilizzare BackgroundWorkerconsiderazioni preliminari se si desidera annullare un thread (chiusura dell'app, cancellazione utente), è necessario decidere se il thread deve verificare la presenza di annullamenti o se deve essere sottoposto all'esecuzione stessa.

BackgroundWorker.CancelAsync()si imposterà CancellationPendingsu truema non farà più nulla, è quindi responsabilità dei thread controllare continuamente questo, tenere presente anche che si potrebbe finire con una condizione di competizione in questo approccio in cui l'utente ha annullato, ma il thread è stato completato prima del test per CancellationPending.

Thread.Abort() d'altra parte genererà un'eccezione nell'esecuzione del thread che impone la cancellazione di quel thread, ma devi stare attento a ciò che potrebbe essere pericoloso se questa eccezione fosse improvvisamente sollevata durante l'esecuzione.

Il threading richiede un'attenta valutazione, indipendentemente dall'attività, per ulteriori letture:

Programmazione parallela nelle migliori pratiche di threading gestito di .NET Framework


5

Sapevo come usare i thread prima di conoscere .NET, quindi mi ci è voluto un po 'per abituarsi quando ho iniziato a usare BackgroundWorkers. Matt Davis ha riassunto la differenza con grande eccellenza, ma aggiungerei che è più difficile comprendere esattamente cosa sta facendo il codice, e questo può rendere più difficile il debugging. È più facile pensare a creare e chiudere thread, IMO, che pensare di dare lavoro a un pool di thread.

Non riesco ancora a commentare i post di altre persone, quindi perdona la mia momentanea zoppia nell'usare una risposta per indirizzare i moli7

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


2

Se non è rotto, aggiustalo finché non ... scherza :)

Ma seriamente BackgroundWorker è probabilmente molto simile a quello che hai già, se avessi iniziato dall'inizio forse avresti risparmiato un po 'di tempo - ma a questo punto non vedo la necessità. A meno che qualcosa non funzioni, o pensi che il tuo codice attuale sia difficile da capire, allora rimarrei con quello che hai.


2

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


2

Voglio sottolineare un comportamento della classe BackgroundWorker che non è stato ancora menzionato. Puoi creare un normale thread da eseguire in background impostando la proprietà Thread.IsBackground.

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

È possibile verificare questo comportamento chiamando il seguente metodo 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 = true; // <--- This will make the thread run in background
    thread.IsBackground = false; // <--- This will delay program termination

    thread.Start();
}

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

Ma quando la proprietà IsBackground è impostata su false (per impostazione predefinita) e chiudi la finestra, allora solo la finestra sparirà ma il processo continuerà comunque a funzionare.

La classe BackgroundWorker utilizza un thread che viene eseguito in background.


1

Un lavoratore in background è una classe che lavora in un thread separato, ma fornisce funzionalità aggiuntive che non si ottengono con un semplice thread (come la gestione dei report sull'avanzamento delle attività).

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


-1

Ciò che mi preoccupa è che il designer di Visual Studio ti consente solo di utilizzare BackgroundWorker e Timer che non funzionano effettivamente con il progetto di servizio.

Ti dà accurati controlli di trascinamento della selezione sul tuo servizio ma ... non provare nemmeno a distribuirlo. Non funzionerà

Servizi: utilizzare solo System.Timers.Timer System.Windows.Forms.Timer non funzionerà anche se è disponibile nella casella degli strumenti

Servizi: BackgroundWorkers non funzionerà quando è in esecuzione come servizio Usa System.Threading.ThreadPools invece o Chiamate asincrone

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.