2016-06-07 58 views
8

In base a MSDN, un riferimento a System.Threading.Timer deve essere conservato altrimenti verrà raccolto automaticamente. Quindi, se corro questo codice, non scrivere alcun messaggio (che è il comportamento previsto):C# Timers and Garbage Collection

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

Tuttavia, se modifico il codice un po 'memorizzando il timer in una variabile locale temporanea, essa sopravvive e scrive il messaggio:

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

Durante la raccolta dei rifiuti, non v'è apparentemente alcun modo come accedere il timer dalla radice o di oggetti statici. Quindi, per favore, puoi spiegare perché il timer sopravvive? Dove viene conservato il riferimento?

+6

Questo certamente * usato * è vero. Sembra che Microsoft abbia finalmente fatto qualcosa al riguardo, migliaia di chiamate di supporto devono aver ispirato. Non sono sicuro di quando sia successo, da qualche parte intorno a 4.5 o 4.6, sospetto. Ora sono mantenuti in vita mentre ticchettano, [TimerQueue.s_queue] (http://referencesource.microsoft.com/#mscorlib/system/threading/timer.cs,75523a07eb2de983) si prende cura di esso. –

+3

Grazie, ma allora il timer dovrebbe essere tenuto in vita in entrambi gli scenari? Perché sopravvive solo al secondo? Sto usando .NET 4.6.1. –

+1

Mi sembra un insetto. Solo il metodo Change() ottiene il timer aggiunto alla coda del timer, il costruttore si dimentica di farlo. È piuttosto difficile immaginare che ciò sia stato intenzionale, prendi in considerazione di segnalarlo. –

risposta

3

Ogni Timer fa riferimento a TimerHolder, che fa riferimento a TimerQueueTimer. L'implementazione mantiene un riferimento interno allo TimerQueueTimer tramite la chiamata a UpdateTimer().

In circostanze normali il vostro timer è libero di essere raccolto, finalizing the TimerHolder e di rimuovere il TimerQueueTimer dalla coda interna. Ma il semplice costruttore Timer(TimerCallback)calls TimerSetup() con lo Timer stesso come stato. Quindi, in questo caso particolare, lo stato di TimerQueueTimer fa riferimento allo Timer, impedendo che venga raccolto.

L'effetto non è correlato alla conservazione di una variabile locale temporanea. Si tratta di solo per il motivo, a causa degli interni del meccanismo Timer. È molto più chiaro e sicuro mantenere un riferimento al timer come raccomandato da MSDN.