2015-12-25 11 views
21

Cosa succede se il programma si chiude in modo imprevisto (per eccezione o il processo è terminato)? Ci sono situazioni come questa (o altro) in cui il programma terminerà, ma gli oggetti IDisposable non verranno smaltiti correttamente?Gli oggetti IDisposable vengono eliminati se il programma viene arrestato in modo imprevisto?

Il motivo per cui lo sto chiedendo è perché sto scrivendo un codice che comunichi con una periferica, e voglio essere sicuro che non ci siano possibilità che rimanga in cattivo stato.

+0

Cosa fare intendi specificamente per "spegnere"? Intendi dire se il potere si spegne o se viene lanciata un'eccezione non gestita? –

+0

Eccezione. So che non c'è nulla di ragionevole che si possa fare se si spegne la corrente (la periferica si resetterà comunque in questo caso). Aggiornerò la domanda – JSideris

+0

@cFrozenDeath Inoltre, intendevo quando il processo è terminato dal sistema operativo (o dall'utente). – JSideris

risposta

12

Se la causa è un'eccezione generata da un blocco using o un blocco try catch finally, verrà eliminata come dovrebbe. Se non viene catturato da un blocco using, non viene eliminato automaticamente (come non accade quando l'applicazione si chiude correttamente).

Un campione:

IDisposable d1 = new X(); 

using (IDisposable d2 = new X()) 
{ 
    throw new NotImplementedException(); 
} 

d1.Dispose(); 

d1 non è disposto, d2 solito è. Alcuni tipi di eccezioni potrebbero impedire la gestione dei blocchi using e alcuni arresti anomali del programma. Se la causa è un'interruzione dell'alimentazione o un arresto anomalo del sistema, non c'è nulla che tu possa fare, naturalmente.

+2

A volte uno scrive un finalizzatore (distruttore C#) per occuparsi dei casi in cui le persone non riescono a chiamare 'Dispose' come nel codice precedente (con' d1'). Nel solito caso in cui viene chiamato 'Dispose', si utilizza' GC.SuppressFinalize (this); 'all'interno di quel metodo per evitare (la maggior parte) il costo delle prestazioni per avere un finalizzatore. Ovviamente nulla di tutto ciò può essere d'aiuto se il processo di applicazione viene ucciso in modo aggressivo dal sistema operativo (i dettagli possono variare qui). –

+0

Intendevo "sistema operativo". –

+0

Anche ricevere un segnale di uccisione non ti consente di fare nulla, anche se non si verifica un arresto anomalo. – Bakuriu

10

Se il programma si interrompe in modo imprevisto (ad esempio, si interrompe il processo), non è assolutamente garantito che venga chiamato il metodo IDisposable.Dispose. Faresti meglio a non fare affidamento su di esso per tali eventi. Il metodo Dispose deve essere chiamato manualmente dal tuo codice, non è qualcosa che il CLR chiamerà automaticamente per te.

+0

saremo in grado di accedere in seguito al file come se fosse bloccato verrà sbloccato? –

+8

Sì, se è un FileStream, il sistema operativo garantirà che l'handle non gestito sottostante verrà liberato quando il processo viene arrestato. Ma questo non significa che verrà chiamato il metodo Dispose. È solo che il sistema operativo sa come reclamare gli handle di file all'uscita del processo. –

4

Sì, ci sono tali situazioni. Ad esempio, chiamando lo , chiamando Environment.FailFast o incontrando un errore CLR interno, il processo verrà chiuso senza eseguire alcun codice aggiuntivo. In tali situazioni, la cosa migliore che puoi fare è dire "vabbè".

Anche se il processo non si chiude inaspettatamente, chiamare Dispose è un'azione manuale. Non è una cosa fatta attraverso il runtime, tranne quando un oggetto che implementa un finalizzatore che chiama Dispose viene sottoposto a garbage collection. Pertanto, dimenticando di avvolgere un monouso in un using o causare una perdita di memoria che mantiene vivo l'oggetto è un altro modo Dispose non può mai essere chiamato.

L'unica pulizia affidabile viene eseguita dal sistema operativo quando un processo termina: tutti gli handle aperti agli oggetti di sistema vengono chiusi. Quando viene chiuso l'ultimo handle, qualsiasi pulizia eseguita nel sistema operativo o nel driver si verifica. Se questo codice di cleanup non fa parte di un driver ma deve essere chiamato da un processo utente, tutto ciò che puoi fare è rendere il tuo codice il più affidabile possibile, o implementare un processo watchdog che gestisca la pulizia per te.

5

un test molto semplice utilizzando un'applicazione console illustrano che Dispose non è chiamato il processo di uccidere:

class DisposableTest : IDisposable 
{ 
    public void Dispose() 
    { 
     Console.WriteLine("Dispose called"); 
    } 
} 

... 

using (DisposableTest sw = new DisposableTest()) 
{ 
    Thread.Sleep(20000); 
} 

Uccidere il processo con Task Manager non farebbe scattare Disposable.Dispose() metodo. Aspettare 20 secondi.

Quindi, come già accennato, non fare affidamento sugli oggetti usa e getta quando l'applicazione si blocca o viene uccisa. Tuttavia, le eccezioni dovrebbero attivarlo. Mi sto solo chiedendo se un'eccezione come StackOverflowException o OutOfMemoryException attiverà sempre Dispose().

[modifica]

appena testato le mie curiosità:

  • StackOverflowException ottiene il processo terminato, quindi non Dispose() è chiamato
  • OutOfMemoryException permette normale chiamata di Dispose()
+0

Grazie per la segnalazione. L'ho appena provato. – Alexei

+0

Ho anche provato questo in passato, ma non sono sicuro che questo sia un test completo. – JSideris

2

IDisposable è solo un'interfaccia. Non c'è assolutamente nulla di speciale nel modo in cui vengono gestiti. Quando chiami Dispose su un IDisposable (esplicitamente o tramite un blocco using), chiama il contenuto del tuo metodo Dispose. Raccoglie i rifiuti come qualsiasi altro oggetto.

Lo scopo dell'interfaccia è consentire a un implementatore di definire la pulizia di un tipo che può avere risorse gestite o non gestite che devono essere rimosse in modo esplicito.

Se queste risorse sono tutte gestite, la garbage collection potrebbe essere sufficiente e le implementazioni potrebbero essere solo per l'ottimizzazione.

Se non sono gestiti o hanno una connessione a risorse non gestite, la garbage collection non è probabilmente sufficiente. Questo è il motivo per cui l'implementazione completa raccomandata di IDisposable comporta la gestione sia dello smaltimento esplicito che dello smaltimento da parte del runtime (tramite un finalizzatore).

Gli arresti del processo non chiamano Dispose ei finalizzatori non sono garantiti per l'esecuzione ... quindi è necessario sperare che la distruzione del processo sia sufficiente da sola.

6

Oltre a Patrick Hofman e Alexei, è possibile che la pulizia della risposta non venga eseguita anche se l'applicazione termina correttamente.

Come probabilmente sapete, il metodo Dispose non viene chiamato quando il garbage collector raccoglie l'oggetto che implementa l'interfaccia IDisposable. Ma il GC chiamerà il metodo Finalize noto anche come finalizzatore. In esso dovresti scrivere la tua logica di pulizia usando Dispose Pattern. E sì, .Net Framework cercherà di eseguire tutti i finalizzatori, ma non c'è garanzia che vengano mai eseguiti.

Ad esempio, il programma qui sotto ha il finalizzatore a lunga durata. Pertanto, .Net terminerà il processo e non vedrai mai il messaggio.

class FinalizableObject 
{ 
    ~FinalizableObject() 
    { 
     Thread.Sleep(50000); 
     Console.WriteLine("Finalized"); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     new FinalizableObject(); 
    } 
} 

Ciò può essere causato da qualsiasi operazione lunga correre come il rilascio di una maniglia di rete o qualche altra cosa che richiederà molto tempo.

Pertanto, non si dovrebbe mai fare affidamento su finalizzatori e oggetti monouso. Ma tutti gli handle aperti agli oggetti del kernel verranno chiusi automaticamente, quindi non dovresti preoccuparti di loro.

mi consiglia di leggere alcuni articoli interessanti sul finalizzatori e il GC, oltre alle risposte:

  1. Everybody thinks about garbage collection the wrong way (Raymond Chen)
  2. When everything you know is wrong, part one (Eric Lippert)
  3. When everything you know is wrong, part two (Eric Lippert)
  4. Terminating a Process (MSDN)
+0

Ottimi articoli da leggere! – JSideris

Problemi correlati