Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
572 views
in Technique[技术] by (71.8m points)

.net - How to properly unload an AppDomain using C#?

I have an application that loads external assemblies which I have no control over (similar to a plugin model where other people create and develop assemblies that are used by the main application). It loads them by creating new AppDomains for these assemblies and then when the assemblies are done being used, the main AppDomain unloads them.

Currently, it simplistically unloads these assemblies by

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

However, on occasion, exceptions are thrown during the unloading process specifically CannotUnloadAppDomainException. From what I understand, this can be expected since a thread in the children AppDomains cannot be forcibly aborted due to situations where unmanaged code is still being executed or the thread is in a finally block:

When a thread calls Unload, the target domain is marked for unloading. The dedicated thread attempts to unload the domain, and all threads in the domain are aborted. If a thread does not abort, for example because it is executing unmanaged code, or because it is executing a finally block, then after a period of time a CannotUnloadAppDomainException is thrown in the thread that originally called Unload. If the thread that could not be aborted eventually ends, the target domain is not unloaded. Thus, in the .NET Framework version 2.0 domain is not guaranteed to unload, because it might not be possible to terminate executing threads.

My concern is that if the assembly is not loaded, then it could cause a memory leak. A potential solution would be to kill the main application process itself if the above exception occurs but I rather avoid this drastic action.

I was also considering repeating the unloading call for a few additional attempts. Perhaps a constrained loop like this:

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

Does this make sense? Should I even bother with trying to unload again? Should I just try it once and move on? Is there something else I should do? Also, is there anything that can be done from the main AppDomain to control the external assembly if threads are still running (keep in mind others are writing and running this external code)?

I'm trying understand what are best practices when managing multiple AppDomains.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I've dealt with a similar problem in my app. Basically, you can't do anything more to force the AppDomain to go down than Unload does.

It basically calls abort of all threads that are executing code in the AppDomain, and if that code is stuck in a finalizer or unmanaged code, there isn't much that can be done.

If, based on the program in question, it's likely that the finalizer/unmanaged code will finish some later time, you can absolutely call Unload again. If not, you can either leak the domain on purpose or cycle the process.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...