Come scaricare correttamente un AppDomain usando C #?

Ho un’applicazione che carica assembly esterni di cui non ho alcun controllo (simile a un modello di plugin in cui altre persone creano e sviluppano gli assembly 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.

Attualmente, questo semplicisticamente scarica questi assembly da

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

Tuttavia, occasionalmente, vengono lanciate eccezioni durante il processo di scarico, in particolare CannotUnloadAppDomainException . Da quello che ho capito, ci si può aspettare perché un thread nei figli AppDomains non può essere forzatamente interrotto a causa di situazioni in cui il codice non gestito è ancora in esecuzione o il thread si trova in un blocco finale :

Quando un thread chiama Unload, il dominio di destinazione viene contrassegnato per lo scaricamento. Il thread dedicato tenta di scaricare il dominio e tutti i thread nel dominio vengono interrotti. Se un thread non si interrompe, ad esempio perché sta eseguendo un codice non gestito o perché sta eseguendo un blocco finally, dopo un periodo di tempo viene lanciata una CannotUnloadAppDomainException nel thread che originariamente si chiamava Unload. Se il thread che non può essere interrotto alla fine termina, il dominio di destinazione non viene scaricato. Pertanto, nel dominio .NET Framework versione 2.0 non è garantito lo scaricamento, poiché potrebbe non essere ansible terminare i thread in esecuzione.

La mia preoccupazione è che se l’assembly non viene caricato, potrebbe causare una perdita di memoria. Una ansible 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 qualche altro tentativo. Forse un ciclo limitato 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 gestisci più AppDomain.

Ho affrontato un problema simile nella mia app. Fondamentalmente, non puoi fare altro per forzare l’ AppDomain a scendere di quanto fa Unload .

Fondamentalmente chiama l’abort di tutti i thread che stanno eseguendo il codice AppDomain e se quel 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, è ansible chiamare nuovamente Unload nuovo. In caso contrario, è ansible perdere il dominio di proposito o ciclare il processo.

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

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