Perché usare try {} finalmente {} con un blocco try vuoto?


239

Ho notato che System.Threading.TimerBase.Dispose()il metodo ha un try{} finally{}blocco ma try{}è vuoto.

C'è valore nell'usare try{} finally{}con un vuoto try?

http://labs.developerfusion.co.uk/SourceViewer/browse.aspx?assembly=SSCLI&namespace=System.Threading&type=TimerBase

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal bool Dispose(WaitHandle notifyObject)
{
    bool status = false;
    bool bLockTaken = false;
    RuntimeHelpers.PrepareConstrainedRegions();
    try {
    }
    finally {
        do {
            if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0) {
                bLockTaken = true;
                try {
                    status = DeleteTimerNative(notifyObject.SafeWaitHandle);
                }
                finally {
                    m_lock = 0;
                }
            }
            Thread.SpinWait(1);
            // yield to processor
        }
        while (!bLockTaken);
        GC.SuppressFinalize(this);
    }

    return status;
}

System.Diagnostics.Process anche sulla linea 2144: riferimentiource.microsoft.com/#System/services/monitoring/…
Patrick Artner,

Risposte:


171

Da http://blog.somecreativity.com/2008/04/10/the-empty-try-block-mystery/ :

Questa metodologia protegge da una chiamata Thread.Abort che interrompe l'elaborazione. La pagina MSDN di Thread.Abort dice che "I blocchi infine non eseguiti vengono eseguiti prima che il thread venga interrotto". Quindi, al fine di garantire che la tua elaborazione finisca anche se il tuo thread viene interrotto nel mezzo da qualcuno che chiama Abort sul tuo thread, puoi inserire tutto il tuo codice nel blocco finally (l'alternativa è scrivere il codice nel blocco "catch" in determinare dove eri prima che "provare" fosse interrotto da Abort e procedere da lì se si desidera).



15
Perché questo non era disponibile fino a .NET 2.0
Hans Passant

6
@ RobFonseca-Ensor: poiché Thread.BeginCriticalRegion()non impedisce l' interruzione di un thread, piuttosto indica al runtime che se un thread viene interrotto, lo stato globale è corrotto e l'intero dominio è soggetto a un omicidio di pietà.
kkm

9
@HansPassant: BeginCriticalSection()davvero non c'era in .NET 1.x, ma non c'è causa ed effetto, che si intende dire perché . In effetti, in .NET 1.x, anche un finallyblocco avrebbe potuto essere interrotto da un thread interrotto. Questi meccanismi hanno uno scopo diverso: svolgere un lavoro in modo da finallyevitare l'interruzione a metà del codice, mentre BeginCriticalSection()al runtime dichiara solo che lo stato globale è a rischio.
kkm

Se lo sviluppatore avesse un po 'di tempo (vero) nel finale, l'interruzione sarebbe mai stata completata o una interruzione tecnica potrebbe essere ignorata indefinitamente?
Max Young,

64

Questo per evitare l' Thread.Abortinterruzione di un processo. La documentazione per questo metodo dice che:

I blocchi infine non eseguiti vengono eseguiti prima che il thread venga interrotto.

Questo perché al fine di recuperare correttamente da un errore, il codice dovrà essere pulito dopo se stesso. Poiché C # non ha distruttori in stile C ++ finallye i usingblocchi sono l'unico modo affidabile per garantire che tale pulizia venga eseguita in modo affidabile. Ricorda che il usingblocco si trasforma in questo dal compilatore:

try {
    ...
}
finally {
    if(obj != null)
        ((IDisposable)obj).Dispose();
}

In .NET 1.x, c'era la possibilità che il finallyblocco venisse interrotto. Questo comportamento è stato modificato in .NET 2.0.

Inoltre, i tryblocchi vuoti non vengono mai ottimizzati dal compilatore.


Grazie per la comprensione del blocco utilizzando.
Stefan

@Anton Capisco che l'utilizzo è una buona pratica. Ma a scopi beffardi, a volte è necessario implementare una classe wrapper e l'oggetto usa e getta diventa una variabile di classe privata. Se rendiamo questo wrapper monouso, GC non gestisce automaticamente la disposizione della variabile di classe privata?
Ozkan,

@Ozkan il GC non elimina automaticamente nulla. Dovresti implementare un finalizzatore, in genere chiamando a Dispose(false);. docs.microsoft.com/en-us/dotnet/standard/garbage-collection/…
Thorarin

@Thorarin mi dispiace ma ti sbagli dicendo che GC non elimina (finalizza) automaticamente.
Ozkan,
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.