2010-12-13 18 views
23

Possiedo un'applicazione che carica assembly esterni su cui non ho controllo (simile a un modello di plug-in in cui altre persone creano e sviluppano assiemi che vengono utilizzati dall'applicazione principale). Li carica creando nuovi AppDomain per questi assembly e quindi quando vengono utilizzati gli assembly, l'AppDomain principale li scarica.Come scaricare correttamente un AppDomain usando C#?

Attualmente, semplicisticamente unloads questi gruppi di

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

Tuttavia, a volte, vengono generate eccezioni durante le operazioni di scarico specificamente CannotUnloadAppDomainException. Da quello che ho capito, questo può essere previsto in quanto un thread nel figli AppDomain cannot be forcibly aborted due to situations where unmanaged code is still being executed or the thread is in a finally block:

Quando un thread chiama scaricamento, il dominio di destinazione è contrassegnato per lo scarico. Il thread dedicato tenta di scaricare il dominio e tutti i thread nel dominio vengono interrotti. Se un thread non non abortire, ad esempio perché è l'esecuzione di codice non gestito, o perché si sta eseguendo un blocco finally, poi dopo un periodo di tempo che un CannotUnloadAppDomainException è gettato nel thread che originariamente chiamato scaricamento. Se il thread che non può essere interrotto alla fine termina, il dominio di destinazione non viene scaricato. Pertanto, nella versione .NET Framework il dominio 2.0 non è garantito per lo scaricamento, poiché potrebbe non essere possibile terminare l'esecuzione dei thread .

La mia preoccupazione è che se l'assembly non viene caricato, potrebbe causare una perdita di memoria. Una possibile soluzione sarebbe quella di uccidere il processo principale dell'applicazione stessa se si verifica l'eccezione sopra, ma piuttosto evito questa azione drastica.

Stavo anche pensando di ripetere la chiamata di scarico per alcuni tentativi aggiuntivi. Forse un ciclo vincolato come questo:

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; 
    } 
} 

Ha senso? Dovrei anche preoccuparmi di provare a scaricare di nuovo? Dovrei provare solo una volta e andare avanti? C'è qualcos'altro che dovrei fare? Inoltre, c'è qualcosa che può essere fatto dall'AppDomain principale per controllare l'assembly esterno se i thread sono ancora in esecuzione (tieni a mente che altri stanno scrivendo ed eseguendo questo codice esterno)?

Sto cercando di capire quali sono le best practice quando si gestiscono più AppDomain.

+4

Non è possibile rispondere. Se non hai alcun controllo sui thread nell'app, non c'è motivo di sperare che uno di quei thread possa cooperare improvvisamente 3 secondi dopo. Isolarlo in un processo è l'unica vera soluzione. –

+0

Grazie Hans. Sembra che questa sia una limitazione di AppDomains rispetto ai processi. Speravo in un modo per uccidere forzatamente un AppDomain in qualsiasi circostanza, proprio come si può uccidere forzatamente un processo (come suggerito). –

+0

@HansPassant L'isolamento dell'esecuzione in un processo dedicato assicura che ciò possa essere evitato? –

risposta

9

Ho avuto a che fare con un problema simile nella mia app. Fondamentalmente, non puoi fare altro per forzare lo AppDomain a scendere di quanto non lo sia lo Unload.

Fondamentalmente chiama l'interruzione di tutti i thread che stanno eseguendo il codice nello AppDomain e, se tale codice è bloccato in un finalizzatore o in un codice non gestito, non c'è molto che possa essere fatto.

Se, in base al programma in questione, è probabile che il finalizzatore/codice non gestito terminerà in un secondo momento, è possibile chiamare nuovamente Unload. In caso contrario, è possibile perdere il dominio di proposito o ciclare il processo.

0

Provare a creare GC.Collect() se non si scarica il dominio.

try 
    { 
     AppDomain.Unload(otherAssemblyDomain); 
    } 
    catch (CannotUnloadAppDomainException) 
    { 
     GC.Collect(); 
     AppDomain.Unload(otherAssemblyDomain); 
    } 
Problemi correlati