Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .Net会垃圾收集一个';未引用,但有一个线程';他在工作吗?_C#_.net_Multithreading_Garbage Collection - Fatal编程技术网

C# .Net会垃圾收集一个';未引用,但有一个线程';他在工作吗?

C# .Net会垃圾收集一个';未引用,但有一个线程';他在工作吗?,c#,.net,multithreading,garbage-collection,C#,.net,Multithreading,Garbage Collection,我有以下代码(为了可读性而删减): 主类: public StartProcess() { Thinker th = new Thinker(); th.DoneThinking += new Thinker.ProcessingFinished(ThinkerFinished); th.StartThinking(); } void ThinkerFinished() { Console.WriteLine("Thinker finished"); } pu

我有以下代码(为了可读性而删减):

主类:

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();
    }
}
思想者类:

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();
    }
}
我最初期望发生的是,主类中的事件处理程序将防止Thinker被垃圾收集

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


换句话说,垃圾收集器是否可以在完成处理之前收集我的思考者?

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

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

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


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

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


这里有一个例子,其中提到了GC的根是什么。为了节省时间,GC根是全局对象、静态对象、所有线程堆栈上的所有引用以及包含引用的所有CPU寄存器。

我同意也不同意,如果对线程对象的引用丢失,线程将被终止并被垃圾回收。在您的情况下,可能不是这样,因为它不是直接使用线程,而是使用计时器。但是,如果您在线程中调用了一个方法,并且该方法结束时线程引用丢失,那么它将由GC收集。您的问题有点难以回答。就像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最终能够完全收集它们。

当计时器上发生已过事件时,它将发生在一个新线程上。也许这就是他所说的。抱歉,我不清楚-被评论的块确实比我认为的更重要。我将尝试更新。另外,正如JoeK所说,我假设计时器本身将运行在不同的线程上。经过的事件肯定会在另一个线程上返回。我希望计时器在它自己的线程上触发它的事件(尽管我非常怀疑这是否有保证,我怀疑规范会允许线程被多个计时器重用),计时器应该通过保持委托活动来保持Thinker实例活动。问题是(我认为)你的线程是否会让思考者保持活力,在不知道你是如何启动线程的情况下,我不确定它是否会。我想如果你可以从你的线程中引用它,那么你也可以,但是正如我所说的,我不确定。是的,根据Joe K上面的评论(以及他给出的链接),计时器似乎使用线程池来引发经过的事件。我想我只是为了安全起见,在主线程中保留对Thinker类的类级引用。我正在查看一些东西,但是计时器应该仍然在运行。这不意味着这个类还活着吗?计时器没有运行。启动计时器时,它会在另一个线程上创建一个对象,并告诉windows在该时间段结束后将其唤醒。当前线程上没有剩余内容。谢谢jrista-其中有一些好东西。作为参考,如果背景线程要完成,它应该几乎立即完成。如果我没有弄错你的答案,计时器应该是预先设定的