Ответ 1
Не обязательно. Устанавливая его на нуль, удаляет любые ссылки на него и полагается на сборщик мусора, чтобы избавиться от него.
Если таймер погас до того, как GC дошел до него, это вызовет событие.
Если у меня есть активный System.Threading.Timer, и я установил его в null, он остановлен?
Я понимаю, что правильнее называть .Dispose()
, но я хотел бы ответить на вопрос, как написано.
public class Foo
{
private System.Threading.Timer _timer;
public Foo()
{
// initialize timer
}
public void KillTimer()
{
_timer=null;
}
}
После многого назад о том, будет ли установка единственной ссылки на System.Threading.Timer на null, действительно приведет к остановке, она показала, что
шип
using System;
using System.Threading;
namespace SO_3597276
{
class Program
{
private static System.Threading.Timer _timer;
static void Main(string[] args)
{
_timer = new Timer((s) => Console.WriteLine("fired"), null, 1000, Timeout.Infinite);
_timer = null;
GC.Collect();
Console.ReadKey();
}
}
}
Обратный вызов таймера не вызывается. Удалите GC.Collect()
и вызывается обратный вызов.
Спасибо всем.
Не обязательно. Устанавливая его на нуль, удаляет любые ссылки на него и полагается на сборщик мусора, чтобы избавиться от него.
Если таймер погас до того, как GC дошел до него, это вызовет событие.
Почему?
Рассмотрим:
System.Threading.Timer t = ...;
System.Threading.Timer q = t;
q = null; // Should this stop it as well?
Настройка на null - это то, что вы делаете с переменной, а не с тем, что вы делаете с объектом. Таймер не может знать, что вы задали конкретную переменную в значение null и не можете предпринимать действия на основе этого.
EDIT:
Чтобы обратиться к редактированию, даже в случае наличия единственной ссылки, не гарантируется, что таймер будет остановлен, так как это возможно, GC может не работать после того, как ссылка была установлена в значение null. Это также не совсем маловероятно: реализация Microsoft.NET использует коллектора поколений, а статическое поле, скорее всего, выживет в детской коллекции и будет переведено в более старшее поколение. Если ваша программа имеет относительно стабильный профиль памяти, никогда не может быть коллекция из более старых поколений (и по расширению финализатор не будет работать до конца программы).
Я знаю, что вы спрашиваете о классе System.Threading.Timer
, но я хочу указать на что-то весьма важное.
Ответы, представленные до сих пор, хороши. Logan и SLaks правильны, что установка любой переменной в null
не оказывает прямого воздействия на объект, к которому переменная была ранее назначена. Рассел прав, что, когда сборщик мусора в конечном итоге утилизирует таймер, он остановится.
SLaks указал, что после установки ссылки на таймер на null
могут существовать затяжные ссылки. В простом примере одной ссылки System.Threading.Timer
это не так.
Но, если у вас есть, например, System.Timers.Timer
, и вы обрабатываете его Elapsed
, а затем установите для параметра null
значение оставить ссылку, а таймер будет продолжать работать навсегда.
Итак, рассмотрите этот код, например:
var t = new System.Timers.Timer(1000.0);
t.AutoReset = true;
t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);
Console.Write("Press Enter to start the timer.");
Console.ReadLine();
t.Start();
Console.Write("Press Enter to set t to null.");
Console.ReadLine();
// This will not stop the timer. It actually does nothing at all to the timer
// to which t has been assigned.
t = null;
Console.Write("Press Enter again to perform a garbage collection.");
Console.ReadLine();
// This STILL will not stop the timer, as t was not the only reference to it
// (we created a new one when we added a handler to the Elapsed event).
GC.Collect();
Console.Write("t is null and garbage has been collected. Press Enter to quit.");
Console.ReadLine();
В приведенном выше примере, поскольку код, удерживающий ссылку на t
для обработки своего события Elapsed
, таймер никогда не останавливается.
Опять же, я понимаю, что это не тот класс, о котором вы спрашивали; Я лишь объясняю это тем, что не всегда очевидно, действительно ли у вас есть или больше нет ссылок на данный объект.
ОБНОВЛЕНИЕ. Похоже, возникла некоторая путаница в вопросе о том, относится ли утверждение, которое я сделал выше, к объекту System.Threading.Timer
; это не. Чтобы убедиться в этом, рассмотрите следующую модификацию приведенного выше кода:
Console.Write("Press Enter to start the timer.");
Console.ReadLine();
var t = new System.Threading.Timer(
state => { Console.WriteLine(DateTime.Now); },
null,
0,
1000
);
Console.Write("Press Enter to set t to null.");
Console.ReadLine();
// This will not stop the timer. It actually does nothing at all to the timer
// to which t has been assigned. HOWEVER, if/when the GC comes around to collect
// garbage, it will see that said timer has no active references; and so it will
// collect (and therefore finalize) it.
t = null;
Console.Write("Press Enter again to perform a garbage collection.");
Console.ReadLine();
// This WILL cause the timer to stop, as there is code in the type's
// finalizer to stop it.
GC.Collect();
Console.Write("t is null and garbage has been collected. Press Enter to quit.");
Console.ReadLine();
Вот почему это не работает для System.Timers.Timer
(или любого типа, который имеет события, фактически):
Ответ очень прост, если мы определили наш обработчик событий следующим образом:
t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);
Что делать, если вместо этого я определил свой обработчик?
t.Elapsed += (sender, e) => Console.WriteLine(sender.GetType());
О, верно! Этот аргумент sender
, на который никто никогда не обращает внимания!
Инфраструктура обработки событий, предоставляемая .NET, требует, чтобы объекты, обрабатывающие события, поддерживали ссылки на объекты, поднимающие события. В противном случае договор, предоставленный самой подписью делегата EventHandler
и всех его кузенов, будет нарушен.
Мораль этой истории: как только вы добавляете обработчик события, вы создали новую ссылку на объект. Единственный способ разрешить сборку этого мусора после этого момента - удалить обработчик, но это может быть довольно сложно, если вы установили единственную ссылку на указанный объект на null
(это один из самых несколько примеров случая, когда .NET-программа может иметь "утечку памяти" ).
Нет, это не остановится.
Установка переменной в null
не имеет непосредственных побочных эффектов, таких как остановка таймера. (Если это не свойство)
Поскольку таймер имеет другие ссылки, GC не будет его собирать, и он никогда не остановится.