2010-12-13 12 views
23

Ich habe eine Anwendung, die externe Baugruppen lädt, die ich nicht kontrollieren kann (ähnlich einem Plugin-Modell, wo andere Leute Baugruppen erstellen und entwickeln, die von der Hauptanwendung verwendet werden). Sie werden geladen, indem neue Anwendungsdomänen für diese Assemblys erstellt werden. Wenn die Assemblys dann fertig sind, werden sie durch die Hauptanwendungsdomäne entladen.Wie wird eine AppDomain mit C# ordnungsgemäß entladen?

Derzeit ist es vereinfachend unloads diese Baugruppen durch

try 
{ 
    AppDomain.Unload(otherAssemblyDomain); 
} 
catch(Exception exception) 
{ 
    // log exception 
} 

jedoch gelegentlich werden Ausnahmen beim Entladevorgang speziell CannotUnloadAppDomainException geworfen. Von dem, was ich verstehe, kann dies, da einen Thread in den Kindern AppDomains cannot be forcibly aborted due to situations where unmanaged code is still being executed or the thread is in a finally block zu erwarten:

Wenn ein Thread ruft Unload, wird das Ziel Domäne zum Entladen markiert. Der dedizierte Thread versucht, die Domäne zu entladen, und alle Threads in der Domäne werden abgebrochen. Wenn ein Thread tut nicht abbrechen, zum Beispiel weil es unmanaged Code ausgeführt wird, oder weil ist es ein finally-Block ausgeführt wird, dann nach einer gewissen Zeit ein CannotUnloadAppDomainException im Thread geworfen ist, die ursprünglich Unload genannt. Wenn der Thread, der nicht abgebrochen werden konnte, schließlich endet, wird die Zieldomäne nicht entladen. So wird in der .NET Framework-Version 2.0 Domäne nicht garantiert entladen werden, da es möglicherweise nicht möglich ist, Threads zu beenden beendet.

Meine Sorge ist, dass wenn die Baugruppe nicht geladen ist, dann könnte es zu einem Speicherverlust führen. Eine mögliche Lösung wäre, den Hauptanwendungsprozess selbst zu beenden, wenn die obige Ausnahme auftritt, aber ich vermeide eher diese drastische Aktion.

Ich erwog auch, den Entladeanruf für ein paar zusätzliche Versuche zu wiederholen. Vielleicht eine eingeschränkte Schleife wie folgt:

try 
{ 
    AppDomain.Unload(otherAssemblyDomain); 
} 
catch (CannotUnloadAppDomainException exception) 
{ 
    // log exception 
    var i = 0; 
    while (i < 3) // quit after three tries 
    { 
     Thread.Sleep(3000);  // wait a few secs before trying again... 
     try 
     { 
      AppDomain.Unload(otherAssemblyDomain); 
     } 
     catch (Exception) 
     { 
      // log exception 
      i++; 
      continue; 
     } 
     break; 
    } 
} 

Macht das Sinn? Sollte ich überhaupt versuchen, wieder zu entladen? Soll ich es einfach mal probieren und weitermachen? Gibt es noch etwas, was ich tun sollte? Gibt es auch etwas, das von der Hauptanwendungsdomäne ausgeführt werden kann, um die externe Assembly zu steuern, wenn die Threads noch ausgeführt werden (bedenken Sie, dass andere diesen externen Code schreiben und ausführen)?

Ich versuche zu verstehen, was Best Practices beim Verwalten mehrerer AppDomains sind.

+4

Dies ist nicht zu beantworten. Wenn Sie keine Kontrolle über die Threads in der App haben, gibt es wenig Grund zu hoffen, dass einer dieser Threads plötzlich 3 Sekunden später kooperieren wird. Es ist die einzige wirkliche Lösung, sie in einem Prozess zu isolieren. –

+0

Danke Hans. Es scheint, dass dies eine Einschränkung von AppDomains im Vergleich zu Prozessen darstellt. Ich habe auf einen Weg gehofft, eine AppDomain unter allen Umständen gewaltsam zu töten, genauso wie Sie einen Prozess gewaltsam beenden können (wie Sie vorgeschlagen haben). –

+0

@HansPassant Ist die Isolierung der Ausführung in einem dedizierten Prozess wirklich gewährleistet, kann dies vermieden werden? –

Antwort

9

Ich habe ein ähnliches Problem in meiner App behandelt. Grundsätzlich können Sie nichts mehr tun, um die AppDomain zu untergehen als Unload tut.

Es ruft grundsätzlich Abbruch aller Threads auf, die Code in dem AppDomain ausführen, und wenn dieser Code in einem Finalizer oder einem nicht verwalteten Code festhängt, kann nicht viel getan werden.

Wenn, basierend auf dem betreffenden Programm, es wahrscheinlich ist, dass der Finalizer/unmanaged Code zu einem späteren Zeitpunkt fertig ist, können Sie unbedingt erneut Unload aufrufen. Ist dies nicht der Fall, können Sie die Domäne entweder absichtlich leasen oder den Prozess zyklisch durchführen.

0

Versuchen Sie, GC.Collect() zu erstellen, wenn Sie die Domäne nicht entladen.

try 
    { 
     AppDomain.Unload(otherAssemblyDomain); 
    } 
    catch (CannotUnloadAppDomainException) 
    { 
     GC.Collect(); 
     AppDomain.Unload(otherAssemblyDomain); 
    } 
Verwandte Themen