6 Stimmen

Wird .Net Garbage Collect ein Objekt, das nicht referenziert ist, aber einen Thread, der Arbeit tut?

Ich habe den folgenden Code (aus Gründen der Lesbarkeit gekürzt):

Hauptklasse:

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

void ThinkerFinished()
{
    Console.WriteLine("Thinker finished");
}

Klasse der Denker:

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

Ursprünglich hatte ich erwartet, dass der Event-Handler in der Hauptklasse verhindern würde, dass der Thinker in den Müll wandert.

Offensichtlich ist dies nicht der Fall .

Ich frage mich nun, ob die Garbage Collection unabhängig davon durchgeführt wird, ob dieser Thread "beschäftigt" ist oder nicht. Mit anderen Worten, besteht die Möglichkeit, dass der Thread vor Ablauf der 5-Sekunden-Zeitüberschreitung gelöscht wird?

Anders gefragt: Ist es möglich, dass der Garbage Collector meinen Thinker einsammelt, bevor er fertig verarbeitet ist?

9voto

olliej Punkte 34163

Nein, ein Thread gilt als live, solange er referenziert wird, und jeder Thread, der läuft, gilt als referenziert (IIRC ein laufender Thread registriert seinen Stack als GC Root, und dieser Stack wird den Thread referenzieren).

Abgesehen davon sehe ich mir dein Beispiel an und verstehe nicht, wo du glaubst, dass ein Thread erzeugt wird?

5voto

Kevin Montrose Punkte 21631

Nein, der Stack eines laufenden Threads fungiert als Root für GC-Zwecke. Dieser Stack lebt so lange, wie der Thread läuft, der Thread selbst wird also nicht gesammelt, solange er läuft.

Hier ist ein Artikel in der (unter anderem) erwähnt wird, was die Wurzeln für GC-Zwecke sind. Um etwas Zeit zu sparen, sind GC-Roots globale Objekte, statische Objekte, alle Referenzen auf allen Thread-Stapeln und alle CPU-Register, die Referenzen enthalten.

3voto

jrista Punkte 31522

Ihre Frage ist ein wenig schwierig zu beantworten. Wie Joel hast du, soweit ich das beurteilen kann, nichts auf dem Stack, was auf deinen Timer verweist, der selbst das einzige ist, was auf den Thread verweist. Angesichts dieser Tatsache würde man erwarten, dass die Thinker-Instanz gesammelt werden würde.

Ich war neugierig darauf und brauchte eine konkretere Erklärung dafür, was passieren könnte, also habe ich mich ein wenig mit Reflector beschäftigt. Es stellte sich heraus, dass System.Timers.Timer letztendlich einen System.Threading.Timer erzeugt, der intern eine Instanz von TimerBase, einer internen Klasse, erzeugt. TimerBase leitet sich von CriticalFinalizerObject ab, einem Systemtyp, der sicherstellt, dass der gesamte Code in einer Constrained Execution Region (CER) ausgeführt wird, bevor die implementierende Klasse vollständig finalisiert und von der GC verworfen wird. TimerBase ist auch IDisposable, und seine Dispose-Methode läuft in einer Schleife und wartet, bis eine Sperre freigegeben wird. An diesem Punkt begann ich, auf externen Code zu stoßen, so dass ich nicht genau weiß, wie die Sperre initialisiert oder freigegeben wird.

Basierend auf der Art und Weise, wie die TimerBase-Klasse geschrieben ist, der Tatsache, dass sie von CriticalFinalizerObject abgeleitet ist, und der Tatsache, dass ihre Dispose-Spinwaits, bis eine Sperre freigegeben wird, denke ich, dass es sicher ist, zu sagen, dass ein Thread, der nicht von etwas referenziert wird, nicht finalisiert wird, bis dieser Code fertig ausgeführt wird. Dennoch ist es wichtig zu beachten, dass er höchstwahrscheinlich von der GC verarbeitet wird... möglicherweise sogar mehr als einmal, da die Finalisierung den Prozess der Auflistung von finalisierten Objekten stark verlängern kann. Bei CriticalFinalizerObjects kann der Finalisierungsprozess sogar noch länger dauern, wenn aktiv Code ausgeführt wird, für den die CER sicherstellt, dass er vollständig ausgeführt wird.

Das könnte bedeuten, dass Sie genau das gegenteilige Problem haben, wenn Ihre Denker eine Weile für die Ausführung brauchen. Anstatt dass diese Objekte vorzeitig eingesammelt werden, werden sie eine langwierige Finalisierung durchlaufen, und alles, worauf sie verweisen, landet in gen2 und lebt eine ganze Weile, bis der GC sie endlich vollständig einsammeln kann.

2voto

Joel Coehoorn Punkte 377088

Wenn ich das richtig verstanden habe (und ich könnte mich auch irren), kann es gesammelt werden, weil es nicht derzeit nichts zu tun.

Wenn Sie lokale Variablen in Ihrer Startmethode hätten und diese Methode noch aktiv wäre, wären diese Variablen immer noch "in scope" auf dem Stack und würden eine Wurzel für Ihren Thread darstellen. Aber die einzige Variable, die Sie verwenden, ist Ihr privater Timer, und da dieser mit dem Thread selbst verwurzelt ist und der Thread nichts auf dem Stack hat, gibt es nichts mehr, um ihn am Leben zu erhalten.

0voto

Shafqat Ahmed Punkte 1912

Ich stimme zu und widerspreche: Wenn der Verweis auf das Thread-Objekt verloren geht, wird der Thread beendet und in den Müll geworfen. In Ihrem Fall ist das vielleicht nicht der Fall, weil Sie nicht direkt mit Threads arbeiten, sondern mit einem Timer. Aber wenn Sie eine Methode in einem Thread aufgerufen hätten und die Thread-Referenz mit dem Ende der Methode verloren gegangen wäre, würde sie von GC gesammelt werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X