Каков объем потока финализатора - для домена приложения или для процесса? - PullRequest
14 голосов
/ 28 октября 2008

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

Я читаю здесь :

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

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

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

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

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

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

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

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

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

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

1 Ответ

8 голосов
/ 28 октября 2008

Похоже, что это действительно всего один поток на экземпляр 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). Вы увидите, что финализатор одного домена приложения блокирует другой.

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

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

...