代码之家  ›  专栏  ›  技术社区  ›  Damovisa

Net会垃圾收集一个没有被引用的对象,但有一个正在工作的线程吗?

  •  6
  • Damovisa  · 技术社区  · 17 年前

    主要类别:

    public StartProcess()
    {
        Thinker th = new Thinker();
        th.DoneThinking += new Thinker.ProcessingFinished(ThinkerFinished);
        th.StartThinking();
    }
    
    void ThinkerFinished()
    {
        Console.WriteLine("Thinker finished");
    }
    

    思考者课程:

    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();
        }
    }
    

    Apparently this isn't the case .

    我现在想知道,不管这个线程是否“忙”,垃圾收集是否会发生。换句话说,是否有可能在5秒超时过期之前对其进行垃圾收集?

    换句话说,垃圾收集器有可能在我的思考者完成处理之前收集它吗?

    5 回复  |  直到 9 年前
        1
  •  9
  •   olliej    17 年前

    不,只要一个线程被引用,它就被认为是活动的,任何正在运行的线程都被认为是被引用的(IIRC一个正在运行的线程将其堆栈注册为GC根,该堆栈将引用该线程)。

    也就是说,我在看你的例子,我不明白你认为线程是从哪里产生的?

        2
  •  5
  •   Kevin Montrose    17 年前

    不,为了GC的目的,运行线程的堆栈充当根。该堆栈将在线程运行时一直存在,因此线程本身不会在其运行时被收集。

    article

        3
  •  3
  •   jrista    17 年前

    你的问题有点难回答。就像Joel一样,据我所知,堆栈上没有引用计时器的内容,而计时器本身是唯一引用线程的内容。有鉴于此,可以预期将收集Thinker实例。

    我对此很好奇,需要一个更具体的解释来解释可能发生的事情,所以我仔细研究了一下反射器。事实证明,System.Timers.Timer最终创建了一个System.Threading.Timer,它在内部创建了一个内部类TimerBase的实例。TimerBase派生自CriticalFinalizerObject,这是一种系统类型,可确保在实现类完全完成并被GC丢弃之前,约束执行区域(CER)中的所有代码都将执行。TimerBase也可以IDisposable,其dispose方法循环并旋转,直到释放锁。在这一点上,我开始运行外部代码,所以我不确定锁是如何初始化或释放的。

    但是,基于TimerBase类的编写方式、它从CriticalFinalizerObject派生的事实以及它的dispose Spin等待锁释放的事实,我认为可以肯定地说,在代码执行完毕之前,没有被任何对象引用的线程将不会被终结。这就是说…重要的是要注意,它很可能会被GC处理…很可能不止一次,因为终结可以大大延长对终结对象的收集过程。对于那些关键的FinalizerObject,如果存在CER确保将完全执行的活动执行代码,则终结过程可能需要更长的时间。

    这可能意味着,如果你的思考者需要一段时间来执行,你就会遇到完全相反的问题。这些对象不会过早地被收集,它们将进入一个漫长的定稿阶段,它们引用的任何东西最终都会在gen2中结束,并在GC最终能够完全收集它们之前存在相当长的一段时间。

        4
  •  2
  •   Joel Coehoorn    17 年前

    如果我读对了(我可能离这里很远),它可以被收集,因为它是 目前正在做任何事情。

    如果start方法中有局部变量,并且该方法仍然处于活动状态,那么这些变量仍然在堆栈上的“作用域”中,并为线程提供根。但是您使用的唯一变量是您的私有计时器,因为它是以线程本身为根的,并且线程在堆栈上没有任何内容,所以没有任何东西可以让它保持活动状态。

        5
  •  0
  •   Shafqat Ahmed    17 年前

    我同意也不同意,如果对thread对象的引用丢失,线程将被终止并被垃圾回收。在您的情况下,可能不是这样,因为它不是直接使用线程,而是使用计时器。但是,如果您在线程中调用了一个方法,并且该方法结束时线程引用丢失,那么它将被GC收集

    推荐文章