Вопрос по c#, .net, appdomain – Попробуйте сделать GC.Collect (), если не выгружаете домен.

22

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

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

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

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

Когда поток вызывает Unload, целевой домен помечается для выгрузки. Выделенный поток пытается выгрузить домен, и все потоки в домене прерываются. Если поток не прерывается, например, потому что он выполняет неуправляемый код или потому, что он выполняет блок finally, то через некоторое время в поток, который первоначально вызывал Unload, выдается исключение CannotUnloadAppDomainException. Если поток, который не может быть прерван, в конце концов заканчивается, целевой домен не выгружается. Таким образом, в домене .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 для управления внешней сборкой, если потоки все еще работают (имейте в виду, что другие пишут и выполняют этот внешний код)?

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

@HansPassant Действительно ли изоляция выполнения от выделенного процесса гарантирует, что этого можно избежать? lysergic-acid
Спасибо, Ганс. Похоже, что это ограничение доменов приложений по сравнению с процессами. Я надеялся на способ принудительного уничтожения домена приложения при любых обстоятельствах, точно так же, как вы можете принудительно завершить процесс (как вы предложили). Ray Vega
Это неопровержимо. Если у вас нет контроля над потоками в приложении, то нет оснований надеяться, что один из этих потоков внезапно начнет работать через 3 секунды. Изолировать его в процессе - единственное реальное решение. Hans Passant
Process.Kill () всегда работает. Hans Passant

Ваш Ответ

2   ответа
9

вы не можете сделать больше, чтобы заставитьAppDomain идти вниз, чемUnload делает.

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

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

0

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

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

Похожие вопросы