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

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

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

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

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

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

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

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

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

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

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

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

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


person Sunny Milenov    schedule 27.10.2008    source источник
comment
+1 за четкий и подробный вопрос   -  person Basic    schedule 13.12.2010


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

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

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

person Jon Skeet    schedule 27.10.2008
comment
Я считаю, что это один поток на экземпляр CLR. Начиная с версии 2.0 и Silverlight, теперь у вас может быть более одной среды CLR для каждого процесса. - person JaredPar; 10.02.2009
comment
@JaredPar - вы правы ... CLR Inside Out: In- Параллельная обработка - person Dave Black; 11.04.2011
comment
@JonSkeet, относительно одного потока финализатора на ядро ​​, теперь, в 2012 году, они это сделали? - person Royi Namir; 03.12.2012
comment
@RoyiNamir: Боюсь, что не знаю. - person Jon Skeet; 03.12.2012