Domanda

Ho una classe chiamata BackgroundWorker che ha un thread costantemente in esecuzione. Per disattivare questo thread, una variabile di istanza denominata stop deve essere true .

Per essere sicuro che il thread sia liberato quando la classe è in uso, ho aggiunto IDisposable e un finalizzatore che invoca Dispose () . Supponendo che stop = true causi davvero la chiusura di questo thread, questo sippet è corretto? Va bene invocare Dispose da un finalizzatore, giusto?

I finalizzatori devono sempre chiamare Dispose se oggetto eredita IDisposable , giusto?

/// <summary>
/// Force the background thread to exit.
/// </summary>
public void Dispose()
{
    lock (this.locker)
    {
        this.stop = true;
    }
}

~BackgroundWorker()
{
    this.Dispose();
}
È stato utile?

Soluzione

Il tuo codice va bene, anche se il blocco in un finalizzatore è in qualche modo "spaventoso". e lo eviterei - se si ottiene una situazione di stallo ... Non sono sicuro al 100% di cosa succederebbe, ma non sarebbe buono. Tuttavia, se sei al sicuro, questo non dovrebbe essere un problema. Soprattutto. Gli interni della raccolta dei rifiuti sono dolorosi e spero che tu non debba mai vederli;)

Come sottolinea Marc Gravell, un bool volatile ti permetterebbe di sbarazzarti della serratura, il che mitigherebbe questo problema. Implementa questa modifica se puoi.

Il codice di nedruod inserisce l'assegnazione all'interno del controllo if (disposing), che è completamente errato: il thread è una risorsa non gestita e deve essere interrotto anche se non esplicitamente disposto. Il tuo codice va bene, sto solo sottolineando che non dovresti seguire i consigli forniti nello snippet di codice.

Sì, quasi sempre dovresti chiamare Dispose () dal finalizzatore se implementi il ??modello IDisposable. Il modello IDisposable completo è un po 'più grande di quello che hai ma non sempre ne hai bisogno - offre semplicemente due possibilità extra:

  1. rilevare se è stato chiamato Dispose () o se il finalizzatore è in esecuzione (non è consentito toccare alcuna risorsa gestita nel finalizzatore, al di fuori dell'oggetto da finalizzare);
  2. abilitando le sottoclassi a sovrascrivere il metodo Dispose ().

Altri suggerimenti

Prima di tutto, un avvertimento grave . Non usare un finalizzatore come te. Ti stai preparando per alcuni effetti molto brutti se prendi i lucchetti all'interno di un finalizzatore. La storia breve è non farlo. Ora alla domanda originale.

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

/// <summary>
/// Force the background thread to exit.
/// </summary>
protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this.locker)
        {
            this.stop = true;
        }
    }
}

~BackgroundWorker()
{
    Dispose(false);
}

L'unico motivo per avere un finalizzatore è consentire alle sottoclassi di estendere e rilasciare risorse non gestite . Se non hai sottoclassi, sigilla la tua classe e elimina completamente il finalizzatore.

Per interesse, qualsiasi motivo per cui non è stato possibile utilizzare il normale BackgroundWorker , che ha il pieno supporto per la cancellazione?

Re the lock - un campo bool volatile potrebbe essere meno problematico.

Tuttavia, in questo caso il tuo finalizzatore non sta facendo nulla di interessante, specialmente dato il "if (smaltimento)" - cioè esegue solo il codice interessante durante Dispose (). Personalmente sarei tentato di restare solo con IDisposable e non fornire un finalizzatore: dovresti ripulirlo con Dispose ().

È " stop " variabile di istanza una proprietà? In caso contrario, non c'è alcun punto particolare nel impostarlo durante il finalizzatore: nulla fa più riferimento all'oggetto, quindi nulla può interrogare il membro.

Se stai effettivamente rilasciando una risorsa, fare in modo che Dispose () e il finalizzatore eseguano lo stesso lavoro (prima testare se il lavoro deve ancora essere svolto) è un buon modello.

È necessario il modello monouso completo ma l'arresto deve essere qualcosa a cui il thread può accedere. Se è una variabile membro della classe che viene eliminata, non va bene perché non può fare riferimento a una classe eliminata. Prendi in considerazione la possibilità di avere un evento di proprietà del thread e di segnalarlo al momento dell'eliminazione.

L'oggetto che implementa il finalizzatore necessita di un riferimento a un flag - memorizzato in un altro oggetto - che il thread sarà in grado di vedere; il thread non deve avere alcun riferimento forte, diretto o indiretto, all'oggetto che implementa il finalizzatore. Il finalizzatore dovrebbe impostare il flag usando qualcosa come CompareExchange, e il thread dovrebbe usare un metodo simile per testarlo. Si noti che se il finalizzatore di un oggetto accede a un altro oggetto, l'altro oggetto potrebbe essere stato finalizzato ma continuerà a esistere. Va bene che un finalizzatore faccia riferimento ad altri oggetti se lo fa in un modo che non sarà disturbato dalla loro finalizzazione. Se tutto ciò che stai facendo è impostare una bandiera, stai bene.

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