Вопрос по .net, c#, multithreading – Будет ли .Net-сборщик мусора собирать объект, на который нет ссылок, но есть поток, который выполняет работу?

6

У меня есть следующий код (сокращен для удобства чтения):

Main Class:

<code>public StartProcess()
{
    Thinker th = new Thinker();
    th.DoneThinking += new Thinker.ProcessingFinished(ThinkerFinished);
    th.StartThinking();
}

void ThinkerFinished()
{
    Console.WriteLine("Thinker finished");
}
</code>

Thinker Class:

<code>public class Thinker
{
    private System.Timers.Timer t;

    public delegate void ProcessingFinished();
    public event ProcessingFinished DoneThinking;

    BackgroundWorker backgroundThread;

    public Thinker() { }

    public StartThinking()
    {
        t = new System.Timers.Timer(5000);    // 5 second timer
        t.AutoReset = false;
        t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
        t.Start();

        // start a background thread to do the thinking
        backgroundThread = new BackgroundWorker();
        backgroundThread.DoWork += new DoWorkEventHandler(BgThread_DoWork);
        backgroundThread.RunWorkerAsync();
    }

    void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        DoneThinking();
    }

    BgThread_DoWork(object sender, DoWorkEventArgs e)
    {
        // work in here should go for much less than 5 seconds
        // it will die if it doesn't

        t.Stop();
        DoneThinking();
    }
}
</code>

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

По-видимому, это не так.

Мне теперь интересно, будет ли сборка мусора происходить независимо от того, "занят" ли эта нить " или нет. Другими словами, есть ли вероятность, что мусор будет собран до истечения 5-секундного тайм-аута?

Иными словами, может ли сборщик мусора собрать мой мыслитель до того, как он закончит обработку?

Ваш Ответ

5   ответов
3

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

Мне было любопытно по этому поводу, и мне нужно было более конкретное объяснение того, что может произойти, поэтому я немного погрузился в Reflector. Как выясняется, System.Timers.Timer в конечном итоге создает System.Threading.Timer, который внутренне создает экземпляр TimerBase, внутреннего класса. TimerBase происходит от CriticalFinalizerObject, который является системным типом, который гарантирует, что весь код в ограниченной области выполнения (CER) будет выполняться до того, как реализующий класс будет полностью завершен и отброшен GC. TimerBase также может быть IDisposable, а его метод dispose зацикливается и вращается, пока не будет снята блокировка. В этот момент я начал работать с внешним кодом, поэтому я не совсем уверен, как блокировка инициализируется или снимается.

Однако, основываясь на том, как написан класс TimerBase, на том факте, что он является производным от CriticalFinalizerObject, и на том факте, что его удаление вращается до снятия блокировки, я думаю, можно с уверенностью сказать, что поток, на который ничего не ссылается, не будет Завершается, пока этот код не будет выполнен. Тем не менее, важно отметить, что он, скорее всего, будет обработан GC ... вполне возможно, более одного раза, так как финализация может значительно удлинить процесс сбора завершенных объектов. Для тех, кто является CriticalFinalizerObjects, процесс завершения может занять еще больше времени, если будет активно выполняться код, который CER гарантирует, что он будет полностью выполнен.

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

Правильно, таймер должен предотвращать преждевременный сбор мусора. (Эта ответственность фактически делегирована System.Threading.TimerBase, поэтому она удалена на несколько градусов, но эффект тот же.)
Спасибо Джриста - там есть кое-что хорошее. Для справки фоновый поток должен завершиться почти сразу, если он вообще завершится. Если я правильно понимаю ваш ответ, таймер должен предотвращать преждевременную сборку мусора? Damovisa
5

стек запущенного потока действует как корень для целей GC. Этот стек будет существовать до тех пор, пока работает поток, поэтому сам поток не будет собираться до тех пор, пока он работает.

Вот этостатья что упоминает (среди прочего), каковы корни для целей GC. Чтобы сэкономить время, корнями GC являются глобальные объекты, статические объекты, все ссылки на все стеки потоков и все регистры ЦП, содержащие ссылки.

0

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

2

это может быть собрано, потому что этоnot в данный момент занимаюсь чем угодно.

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

Что-то, на что я смотрю, но Таймер все еще должен работать. Разве это не значит, что класс все еще жив? Damovisa
Таймер не работает. Когда вы запускаете таймер, он создает объект в другом потоке и указывает окнам активировать его, когда истекает период. В текущей теме ничего не осталось.
8

поток считается живым до тех пор, пока на него ссылаются, а любой работающий поток считается ссылочным (IIRC - работающий поток регистрирует свой стек как корень GC, и этот стек будет ссылаться на поток).

Тем не менее, я смотрю на ваш пример и не понимаю, где, по вашему мнению, создается нить?

Кроме того, что сказал Джо К - сам Таймер, как я предполагал, будет работать в другом потоке. Событие Elapsed, безусловно, возвращается в другой поток. Damovisa
Извините, мне было не ясно - прокомментированный кусок действительно важнее, чем я считал. Я попытаюсь обновить. Damovisa
Да, на основании приведенного выше комментария Джо К. (и ссылки, которую он дал), похоже, что Timer использует ThreadPool для вызова событий Elapsed. Я думаю, что я просто буду в безопасности и сохраню ссылку на уровне класса на класс Thinker из основного потока. Damovisa
Когда событие Elapsed на таймере происходит, оно происходит в новом потоке. Может быть, это то, о чем он говорит.msdn.microsoft.com/en-us/library/system.timers.timer.aspx
Я ожидал бы, что Timer запускает свое событие в своем собственном потоке (хотя я очень сомневаюсь, что это гарантировано, я подозреваю, что спецификация позволит повторно использовать потоки для нескольких таймеров), таймер должен поддерживать экземпляр Thinker живым путем оставляя делегата в живых. Вопрос состоит в том (я думаю), будет ли ваш поток поддерживать мыслитель, и, не зная, как вы запускаете свой поток, я не уверен, будет ли это так. Я полагаю, что если вы можете сослаться на это из своей ветки, то можете, но, как я уже сказал, я не уверен.

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