Подтвердить что ты не робот

Как правильно выгрузить AppDomain с помощью С#?

У меня есть приложение, которое загружает внешние сборки, которые я не контролирую (аналогично модели плагина, где другие люди создают и разрабатывают сборки, которые используются основным приложением). Он загружает их, создавая новые AppDomains для этих сборок, а затем, когда сборки выполняются, основной AppDomain выгружает их.

В настоящее время упрощенно выгружает эти сборки

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

Однако иногда исключения выбрасываются во время процесса разгрузки, в частности CannotUnloadAppDomainException. Из того, что я понимаю, этого можно ожидать, поскольку поток в дочерних приложениях AppDomains не может быть принудительно отменен из-за ситуаций, когда неуправляемый код все еще выполняется или поток находится в finally:

Когда поток вызывает Unload, целевой домен помечен для разгрузки. выделенный поток пытается выгрузить домен и все потоки в домен прерван. Если поток не прерывается, например, потому что это выполнение неуправляемого кода или он выполняет блок finally, затем после некоторого периода времени CannotUnloadAppDomainException - это брошенный в поток, который изначально вызывается Unload. Если поток, который не может быть прервано в конце концов, целевой домен не выгружается. Таким образом, в версии .NET Framework 2.0 домен не может быть выгружен, потому что он может не быть возможно прекратить выполнение потоки.

Меня беспокоит, что если сборка не загружена, это может вызвать утечку памяти. Потенциальным решением было бы уничтожить сам основной процесс приложения, если это исключение произошло, но я скорее избегу этого радикального действия.

Я также рассматривал возможность повторения разгрузочного вызова для нескольких дополнительных попыток. Возможно, такой замкнутый цикл:

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;
    }
}
Это имеет смысл? Должен ли я даже беспокоиться о попытке разгрузить? Должен ли я просто попробовать один раз и двигаться дальше? Есть ли что-то еще, что я должен делать? Кроме того, есть ли что-либо, что можно сделать из основного AppDomain для управления внешней сборкой, если потоки все еще работают (помните, что другие пишут и запускают этот внешний код)?

Я пытаюсь понять, что лучше всего подходит для управления несколькими AppDomains.

4b9b3361

Ответ 1

Я применил аналогичную проблему в своем приложении. В принципе, вы не можете больше ничего сделать, чтобы заставить AppDomain спуститься ниже Unload.

В основном он вызывает прерывание всех потоков, выполняющих код в AppDomain, и если этот код застрял в финализаторе или неуправляемом коде, этого не может быть сделано.

Если, основываясь на рассматриваемой программе, вероятно, что финализатор/неуправляемый код закончится позже, вы можете снова называть Unload снова. Если нет, вы можете либо протекать в домене по назначению, либо выполнить цикл процесса.

Ответ 2

Попробуйте сделать GC.Collect(), если вы не выгрузите домен.

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