2016-06-07 15 views
8

Zgodnie z MSDN należy zachować odniesienie do System.Threading.Timer, w przeciwnym razie zostanie pobrane śmieci. Więc jeśli ten kod, to nie pisać żadnych wiadomości (co jest oczekiwane zachowanie):C# Timery i usuwanie śmieci

static void Main(string[] args) 
{ 
    RunTimer(); 
    GC.Collect(); 
    Console.ReadKey(); 
} 

public static void RunTimer() 
{ 
    new Timer(s => Console.WriteLine("Hello"), null, TimeSpan.FromSeconds(1), TimeSpan.Zero); 
} 

Jeśli jednak zmodyfikować kod nieznacznie przechowując timer w tymczasowej zmiennej lokalnej, to przetrwa i zapisuje komunikat:

public static void RunTimer() 
{ 
    var timer = new Timer(s => Console.WriteLine("Hello")); 
    timer.Change(TimeSpan.FromSeconds(1), TimeSpan.Zero); 
} 

Podczas usuwania śmieci najwyraźniej nie ma sposobu uzyskania dostępu do licznika z katalogu głównego lub obiektów statycznych. Czy możesz wyjaśnić, dlaczego czasomierz przetrwał? Gdzie jest zachowane odniesienie?

+6

To z pewnością * używane *, aby było prawdziwe. Wygląda na to, że Microsoft w końcu coś z tym zrobił, tysiące zgłoszeń do wsparcia musiało być inspirujących. Nie jestem pewien, kiedy to się stało, gdzieś około 4,5 lub 4,6, podejrzewam. Są teraz utrzymywane przy życiu, gdy tykają, [TimerQueue.s_queue] (http://referencesource.microsoft.com/#mscorlib/system/threading/timer.cs,75523a07eb2de983) zajmuje się tym. –

+3

Dzięki, ale czy w obu scenariuszach powinien być utrzymany zegar? Dlaczego przetrwa tylko drugi? Używam .NET 4.6.1. –

+1

To wygląda mi na błąd. Tylko metoda Change() pobiera licznik dodany do kolejki timera, konstruktor zapomina o tym. Trudno sobie wyobrazić, że było to celowe, rozważ zgłoszenie. –

Odpowiedz

3

Każdy numer Timer odnosi się do TimerHolder, który odnosi się do TimerQueueTimer. Implementacja zachowuje wewnętrzne odwołanie do TimerQueueTimer poprzez wywołanie do UpdateTimer().

W normalnych okolicznościach można odebrać licznik, finalizing the TimerHolder i usunąć TimerQueueTimer z wewnętrznej kolejki. Ale prosty konstruktor sam w sobie jest stanem. W tym konkretnym przypadku stan TimerQueueTimer odwołuje się do Timer, uniemożliwiając jego zbieranie.

Efekt nie jest związany z utrzymywaniem tymczasowej zmiennej lokalnej. To tak się dzieje do pracy ze względu na elementy wewnętrzne mechanizmu Timer. O wiele bardziej czytelne i bezpieczne jest odwoływanie się do licznika czasu zgodnie z zaleceniami MSDN.