2016-06-07 3 views
8

Согласно MSDN, ссылка на System.Threading.Timer должна храниться в противном случае, она получит сбор мусора. Так что, если я запускаю этот код, он не пишет никаких сообщений (что ожидаемое поведение):C# Таймеры и сбор мусора

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

Однако, если я немного изменить код, храня таймера во временной локальной переменной, она выживает и пишет сообщение:

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

Во время сбора мусора, там, видимо, нет способа, как получить доступ к таймеру от корня или статических объектов. Так вы можете объяснить, почему таймер выживает? Где сохраняется эта ссылка?

+6

Это обязательно * используется * чтобы быть правдой. Похоже, что Microsoft наконец-то что-то с этим поделала, тысячи вызовов поддержки должны были вдохновлять. Не уверен, когда это произошло, где-то около 4.5 или 4.6 я подозреваю. Теперь они сохраняются в живых, поскольку они тикают, [TimerQueue.s_queue] (http://referencesource.microsoft.com/#mscorlib/system/threading/timer.cs,75523a07eb2de983) позаботится об этом. –

+3

Спасибо, но нужно ли сохранять таймер в обоих сценариях? Почему он выживает только второй? Я запускаю .NET 4.6.1. –

+1

Это похоже на ошибку. Только метод Change() получает таймер, добавленный в очередь таймера, конструктор забывает это сделать. Довольно трудно представить, что это было намеренно, подумайте об этом. –

ответ

3

Каждый Timer ссылки a TimerHolder, который ссылается на TimerQueueTimer. Реализация сохраняет внутреннюю ссылку на TimerQueueTimer по вызову UpdateTimer().

В нормальных условиях ваш таймер можно бесплатно собрать, finalizing the TimerHolder и удалить TimerQueueTimer из внутренней очереди. Но простой конструктор Timer(TimerCallback)calls TimerSetup() с Timer сам по себе как состояние. Поэтому в данном конкретном случае состояние TimerQueueTimer ссылается на Timer, что предотвращает его сбор.

Эффект не связан с сохранением временной локальной переменной. Это просто так случилось работать из-за внутренних компонентов механизма Timer. Гораздо яснее и безопаснее хранить ссылку на ваш таймер, как рекомендовано MSDN.

Смежные вопросы