Kdy se neprovádí blok finally?

Tomáš Holan       7. 3. 2012             5845 zobrazení

To že, u konstruktu try-catch-finally v programovacím jazyce C# se blok finally provádí v případě, kdy vznikne při běhu kódu uvnitř try bloku výjimka, i v případě, kdy kód uvnitř try bloku proběhne korektně je skutečnost jistě velmi dobře známá. Existují ale nějaké případy, kdy se blok finally neprovádí?

Ano, určitě existují a lze jich najít asi hned několik, například jsou jimi některé výjimky, které vzniknou přímo v CLR jako například StackOverflowException. Při této výjimce je proces okamžitě ukončen bez jakékoliv šance nějak zasáhnout tj. nejsou spouštěny finally bloky, AppDomain.UnhandledException handler, CER (Constrained Execution Regions) bloky ani kritické finalizery.

Další takový případ, který si ukážeme, je možný ve spojení s chováním .NET aplikací při neošetřené výjimce. Mějme například následující konzolovou aplikaci:

public static void Main()
{
    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

    try
    {
        Console.WriteLine("try 1:");
        try
        {
            Console.WriteLine("\ttry 2:");
            try
            {
                Console.WriteLine("\t\ttry 3:");

                throw new Exception("Unhandled exception");
            }
            finally
            {
                Console.WriteLine("\t\tfinally block (3)");
            }
        }
        catch
        {
            Console.WriteLine("\tcatch block (2)");
            throw;
        }
        finally
        {
            Console.WriteLine("\tfinally block (2)");   //<--Nebude spuštěn
        }
    }
    finally
    {
        Console.WriteLine("finally block (1)"); //<--Nebude spuštěn
    }
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    Console.WriteLine("CurrentDomain_UnhandledException: " + ((Exception)e.ExceptionObject).Message);
    Environment.FailFast(null); //Zákaz spuštění finally bloků
}

Na úrovni aplikační domény zde máme zaregistrovanou událost UnhandledException. Tato událost dovoluje reagovat na neošetřenou výjimku před tím, než v jejím důsledku dojde k ukončení celého procesu (k tomu v tomto případě dojde, protože výjimka vzniká v hlavním vlákně, které je jediné vlákno na popředí). V tomto handleru jsou možné reakce na výjimku již ale poměrně omezené, například ukončení procesu již zabránit nelze. Co ale lze, je zakázat spuštění finally bloků, a to konkrétně zavoláním metody Environment.FailFast.

Výstup uvedeného příkladu po spuštění z příkazové řádky bude vypadat takto:

Crash

Je vidět, že ke spuštění finally bloků 2 a 1 opravdu nedošlo (u finally bloků 3 ano).

Proč tomu tak je?

V případě vyhození výjimky ve skutečnosti .NET neprovádí pouze jeden, ale dva nezávislé průchody volání na zásobníku. Při prvním průchodu se pouze zjišťuje, zda je daná výjimka ošetřená nebo nikoliv tj. hledá se nějaký catch blok odpovídající danému typu výjimky (v našem případě je nalezen catch blok 2). Pokud se najde, je teprve v druhém průchodu prováděn kód všech odpovídajících finally bloků (v našem případě jen finally blok 3) a nakonec kód původně nalezeného catch bloku. Při opakovaném vyhození výjimky uvnitř catch bloku je tento proces spuštěn znovu, ale až od tohoto místa opakovaného vyhození.

Pokud není v prvním průchodu žádný odpovídající catch blok nalezen, je výjimka považována za neošetřenou, druhý průchod je zatím odložen a runtime nejprve provádí další akce. První z nich je zobrazení WER (Windows Error Reporting) dialogu a druhou je spuštění UnhandledException handleru. Druhý průchod tj. spuštění ostatních finally bloků (v našem případě by se jednalo o finally blok 2 a 1) by byl proveden až po uzavření WER dialogu a pouze v případě, kdy by nebylo provedeno volání metody FailFast v handleru.

(Jako podklady pro tento příspěvek mi posloužili komentáře v diskuzi pod článkem zde.)

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback