2013-08-08 18 views
16

Wenn Sie ein Timer oder ein Thread, die für die gesamte Laufzeit des Programms führen Sie einfach tun Sie ihnen einen Verweis zu halten brauchen, um sie zu verhindern, dass Müll gesammelt werden?Können Timer automatisch Müll gesammelt werden?

Bitte vergessen Sie nicht, dass das folgende Programm timer als statische Variable in der Klasse haben könnte, dies ist nur ein Spielzeugbeispiel, um das Problem zu zeigen.

public class Program 
{ 
    static void Main(string[] args) 
    { 
     CreateTimer(); 

     Console.ReadLine(); 
    } 

    private static void CreateTimer() 
    { 
     var program = new Program(); 

     var timer = new Timer(); 
     timer.Elapsed += program.TimerElapsed; 
     timer.Interval = 30000; 
     timer.AutoReset = false; 
     timer.Enabled = true; 
    } 

    private void TimerElapsed(object sender, ElapsedEventArgs e) 
    { 
     var timerCast = (Timer)sender; 

     Console.WriteLine("Timer fired at in thread {0}", GetCurrentThreadId()); 

     timerCast.Enabled = true; 
    } 

    ~Program() 
    { 
     Console.WriteLine("Program Finalized"); 
    } 

    [DllImport("kernel32.dll")] 
    static extern uint GetCurrentThreadId(); 
} 

Kann der Timer in diesem obigen Beispiel gesammelt werden? Ich lief es für eine Weile und ich bekam nie eine Ausnahme noch eine Nachricht, die ~Program() genannt wurde.

UPDATE: fand ich von this question aus (thanks sethcran), die Fäden von der CLR verfolgt werden, aber ich würde immer noch eine Antwort über Timer mögen.

+1

Welchen 'Timer'-Typ verwenden Sie hier? WinForms, Threading, etc ... – JaredPar

+0

@JaredPar Irgendwelche von ihnen (aber ich habe 'System.Timers' im obigen Beispiel benutzt). Ich bin auch neugierig, ob ich einen Verweis auf alle Threads behalten muss, die ich unter Verwendung von 'Thread' –

+0

http: // stackoverflow erstellt habe.com/questions/9011831/new-thread-and-waste-collection – Sethcran

Antwort

36

Dies ist nur ein Problem mit der System.Threading.Timer-Klasse, wenn Sie nicht anderweitig eine Referenz darauf speichern. Es hat mehrere Konstruktorüberladungen, die, die das Zustand Objekt nehmen, sind wichtig. Die CLR beachtet dieses Zustandsobjekt. Solange es irgendwo referenziert wird, behält die CLR den Timer in seiner Timer-Warteschlange und das Timer-Objekt wird keine Müllsammlung erhalten. Die meisten Programmierer werden dieses Statusobjekt nicht verwenden, der MSDN-Artikel erklärt seine Rolle sicherlich nicht.

System.Timers.Timer ist ein Wrapper für die System.Threading.Timer Klasse, es einfacher zu bedienen. Insbesondere wird es dieses Statusobjekt verwenden und einen Verweis darauf behalten, solange der Timer aktiviert ist.

Beachten Sie, dass in Ihrem Fall, die der Timer Enabled-Eigenschaft falsch ist, wenn es Ihre verstrichene Ereignishandler eintritt, weil Sie Autoreset = false haben. Daher ist der Timer für die Erfassung verfügbar, sobald er in Ihren Ereignishandler eintritt. Aber Sie bleiben aus der Klemme, indem Sie auf das Absender Argument verweisen, das erforderlich ist, um Enabled wieder auf true zu setzen. Dadurch wird der Jitter als Referenz angezeigt, sodass Sie kein Problem haben.

Seien Sie vorsichtig mit dem Ereignishandler Elapsed. Jede Ausnahme, die in dieser Methode ausgelöst wird, wird ohne Diagnose verschluckt. Das bedeutet auch, dass Sie Enabled nicht auf true setzen. Sie müssen verwenden versuchen/fangen, etwas Vernünftiges zu tun. Wenn Sie Ihr Programm nicht absichtlich beenden, müssen Sie Ihrem Hauptprogramm zumindest mitteilen, dass etwas nicht mehr funktioniert. Wenn Enabled = true in einer finally-Klausel verwendet wird, kann vermieden werden, dass der Timer-Garbage Collector gesammelt wird, aber auf die Gefahr hin, dass Ihr Programm immer wieder Exceptions auslöst.

+1

+1. Besonders gefiel mir, dass du das verrückte Ausnahmesquashing erwähnt hast, was der Hauptgrund ist, warum ich 'System.Timers.Timer' nicht verwenden werde. –

+1

Qualität Antwort ... Noch _again_. – MoonKnight

+0

Wenn ich eine Threading.Timer-Instanz mit Nicht-Null-Status für ** einzelnen ** Callback-Aufruf (Zeitraum ist "Timeout.InfiniteTimeSpan") verwenden, dann kann GC den Timer nach dem Aufruf ohne Probleme sammeln? – Mergasov

3

Fügen Sie diesen Code einem Programm hinzu und führen Sie es aus. Sie werden sehen, dass der Timer nicht erfasst wird.

private void DoStuff() 
    { 
     CreateTimer(); 
     Console.WriteLine("Timer started"); 
     int count = 0; 
     for (int x = 0; x < 1000000; ++x) 
     { 
      string s = new string("just trying to exercise the garbage collector".Reverse().ToArray()); 
      count += s.Length; 
     } 
     Console.WriteLine(count); 
     Console.Write("Press Enter when done:"); 
     Console.ReadLine(); 
    } 

    private void Ticktock(object s, System.Timers.ElapsedEventArgs e) 
    { 
     Console.WriteLine("Ticktock"); 
    } 

    private void CreateTimer() 
    { 
     System.Timers.Timer t = new System.Timers.Timer(); // Timer(Ticktock, null, 1000, 1000); 
     t.Elapsed += Ticktock; 
     t.Interval = 1000; 
     t.AutoReset = true; 
     t.Enabled = true; 
    } 

So ist die Antwort auf Ihre Frage scheint zu sein, dass der Timer nicht für Sammlung und nicht gesammelt werden, wenn Sie nicht über einen Verweis auf sie halten.

Es ist interessant zu beachten, dass wenn Sie den gleichen Test mit System.Threading.Timer ausführen, werden Sie feststellen, dass der Timer ist gesammelt.

+0

Sie haben die falsche Art von Timer. –

+0

@HansPassant: Danke. Ich habe den Test mit 'System.Timers.Timer' erneut durchgeführt und festgestellt, dass er nicht gesammelt wurde. Interessant. –

Verwandte Themen