Какова область действия финализатора - для домена приложения или для каждого процесса?

Основываясь на всех моих чтениях, должен быть один поток GC для вызова всех финализаторов. Теперь вопрос заключается в том, какова область действия этого "одного" потока - для каждого процесса или для каждого домена приложения, поскольку все намерение доменов состоит в том, чтобы разделить и сделать "независимые" различные приложения в одном пространстве процесса.

Я прочитал здесь:

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

Более серьезно, что происходит если ваш финализатор не выходит по какой-то причине, например, он блокирует, ожидая условия, которые никогда не имеет место. В этом случае финализатор нить будет висела, так что больше нет финализируемые объекты будут мусором собрано. Ты должен быть очень осознавая эту ситуацию и придерживайтесь написав простейший код, чтобы освободить неуправляемые ресурсы в финализаторах.

Еще одно соображение - это то, что происходит во время выключения приложения. Когда программа отключается, сборщик мусора будут пытаться назвать финализаторы всех конечных объектов, но с определенные ограничения:

  • Конечные объекты не рекламируются к более высоким поколениям кучи во время завершение работы.

  • Любой отдельный финализатор будет иметь максимум 2 секунды для выполнения; если оно занимает больше времени, он будет убит.

  • Максимум 40 секунд для все финализаторы должны быть выполнены; если таковые имеются финализаторы все еще выполняются или ожидая в этот момент процесс внезапно уничтожается.

Слишком много сообщений (и даже официальная документация) неправильное использование терминов "приложение", "процесс" и "домен приложения" - большинство из них даже полагают, что они равны, поскольку обычно приложения запускаются в одном домене приложения в единый процесс. Это неправильное использование делает все эти документы трудными для чтения и даже не полезно.

Итак, мой вопрос предполагает более одного приложения, каждый из которых выполняется в отдельном домене приложения в одном процессе.

Все ли эти приложения используют одни и те же потоки GC и финализатора? Означает ли проблема, описанная в статье выше (зависает поток финализатора), затронет все приложения в этом процессе? Если да - есть ли способ обхода (кроме того, чтобы не использовать плохие приложения), как-то обнаружить поток финализатора и отправить его Thread.Abort?

Все выше, потому что я ударил аналогичную проблему. Мое приложение работает в отдельном домене приложения как дополнение к стороннему программному обеспечению (Outlook). Из-за различных причин мне нужно вызвать GC.Collect и GC.WaitForPendingFinalizers для полного выпуска ссылок COM (обычных процедур взаимодействия недостаточно для Office/Outlook), когда работает какая-либо другая сторонняя добавка, мой GC.WaitForPendingFinalizers висит навсегда, поэтому я подозреваю, что в этой третьей стороне есть "плохой" финализатор. У меня нет контроля над заменой/удалением этого добавления (требование клиента), поэтому я должен сам выяснить, как сделать их совместными.

Ответы

Ответ 1

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

Test.cs:

using System;

class Test
{
    static void Main()
    {
        AppDomain.CreateDomain("First")
                 .ExecuteAssembly("ShowFinalizerThread.exe");
        AppDomain.CreateDomain("Second")
                 .ExecuteAssembly("ShowFinalizerThread.exe");
    }
}

ShowFinalizerThread.cs:

using System;
using System.Threading;

class ShowFinalizerThread
{
    static Random rng = new Random();

    ~ShowFinalizerThread()
    {
        Console.WriteLine("Thread/domain: {0}/{1}",
                          Thread.CurrentThread.ManagedThreadId,
                          AppDomain.CurrentDomain.FriendlyName);
        if (rng.Next(10) == 0)
        {
            Console.WriteLine("Hanging!");
            Thread.Sleep(2000);
        }
    }

    static void Main()
    {
        new Thread(LoopForever).Start();
    }

    static void LoopForever()
    {
        while (true)
        {
            new ShowFinalizerThread();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Thread.Sleep(300);
        };
    }
}

Скомпилируйте их как консольное приложение, затем запустите test.exe(из командной строки проще всего, IMO). Вы увидите, что один финализатор домена приложения блокирует другой.

В будущем я бы не удивился, увидев один поток финализатора на ядро, а не на AppDomain, но похоже, что у вас все еще будут проблемы: (

У вас есть мое глубочайшее сочувствие (хотя это и не решение) - как только я обнаружил тупик в Oracle Blob. Мы смогли это исправить, правильно распорядившись, но я знаю, что не все работает так красиво - и это была настоящая боль, даже если вы нашли это!