Warum try {} finally {} mit einem leeren try-Block verwenden?

239

Mir ist aufgefallen, dass System.Threading.TimerBase.Dispose()die Methode einen try{} finally{}Block hat, der try{}aber leer ist.

Gibt es einen Wert bei der Verwendung try{} finally{}mit einem leeren 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;
}
Samuel Neff
quelle
System.Diagnostics.Process um Linie 2144 auch: referencesource.microsoft.com/#System/services/monitoring/…
Patrick Artner

Antworten:

171

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

Diese Methode schützt vor einem Thread.Abort-Aufruf, der die Verarbeitung unterbricht. Auf der MSDN-Seite von Thread.Abort heißt es: "Nicht ausgeführte endgültige Blöcke werden ausgeführt, bevor der Thread abgebrochen wird." Um sicherzustellen, dass Ihre Verarbeitung auch dann abgeschlossen ist, wenn Ihr Thread in der Mitte von jemandem abgebrochen wird, der Abort in Ihrem Thread aufruft, können Sie Ihren gesamten Code in den finally-Block einfügen (die Alternative besteht darin, Code in den "catch" -Block zu schreiben Bestimmen Sie, wo Sie sich befanden, bevor "Versuch" durch Abbruch unterbrochen wurde, und fahren Sie von dort fort, wenn Sie möchten.

danben
quelle
6
Warum nicht msdn.microsoft.com/en-us/library/… verwenden ?
Rob Fonseca-Ensor
15
Weil das erst verfügbar war, als .NET 2.0
Hans Passant
6
@ RobFonseca-Ensor: Weil Thread.BeginCriticalRegion()nicht verhindert wird, dass ein Thread abgebrochen wird, sondern der Laufzeit mitgeteilt wird, dass der globale Status beschädigt ist , wenn ein Thread abgebrochen wird, und die gesamte Appdomain einem Gnadenmord ausgesetzt ist.
km
9
@HansPassant: war BeginCriticalSection()wirklich nicht in .NET 1.x vorhanden, aber es gibt keine Ursache und Wirkung, die Sie implizieren, wenn Sie sagen, weil . In .NET 1.x könnte sogar ein finallyBlock durch einen Thread-Abbruch unterbrochen worden sein. Diese Mechanismen dienen einem anderen Zweck: Durch Arbeiten in a wird ein finallyAbbruch auf halbem Weg im Code verhindert, während BeginCriticalSection()nur zur Laufzeit erklärt wird, dass der globale Status gefährdet ist.
km
Wenn der Entwickler eine Weile (wahr) in der Endphase hat, würde der Abbruch jemals abgeschlossen sein oder könnte ein Abbruch technisch auf unbestimmte Zeit ignoriert werden?
Max Young
64

Dies soll verhindern, dass Thread.Abortein Prozess unterbrochen wird. Die Dokumentation für diese Methode besagt Folgendes:

Nicht ausgeführte endgültige Blöcke werden ausgeführt, bevor der Thread abgebrochen wird.

Dies liegt daran, dass Ihr Code nach sich selbst bereinigt werden muss, um einen Fehler erfolgreich zu beheben. Da C # keine Destruktoren im C ++ - Stil hat finallyund usingBlöcke die einzige zuverlässige Möglichkeit sind, um sicherzustellen, dass eine solche Bereinigung zuverlässig durchgeführt wird. Denken Sie daran, dass usingder Compiler diesen Block in diesen verwandelt:

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

In .NET 1.x bestand die Möglichkeit, dass der finallyBlock abgebrochen wird. Dieses Verhalten wurde in .NET 2.0 geändert.

Darüber hinaus werden leere tryBlöcke vom Compiler niemals optimiert.

Anton Gogolev
quelle
Vielen Dank für den Einblick in den using-Block.
Stefan
@Anton Ich verstehe, dass die Verwendung eine bewährte Methode ist. Zu Verspottungszwecken muss jedoch manchmal eine Wrapper-Klasse implementiert werden, und das verfügbare Objekt wird zu einer privaten Klassenvariablen. Wenn wir diesen Wrapper verfügbar machen, übernimmt GC dann nicht automatisch die Entsorgung der privaten Klassenvariablen?
Ozkan
@ Ozkan der GC entsorgt nichts automatisch. Sie müssten einen Finalizer implementieren, der normalerweise a aufruft Dispose(false);. docs.microsoft.com/en-us/dotnet/standard/garbage-collection/…
Thorarin
@Thorarin Entschuldigung, aber Sie liegen falsch, wenn Sie sagen, dass GC nicht automatisch entsorgt (finalisiert).
Ozkan