简体   繁体   中英

does nulling a System.Threading.Timer stop it?

If I have an active System.Threading.Timer and I set it to null, is it stopped?

I realize that it is more proper to call .Dispose() but I would like an answer to the question as written.

public class Foo
{
   private System.Threading.Timer _timer;
   public Foo()
   {
      // initialize timer
   }

   public void KillTimer()
   {
      _timer=null;
   }
}

Update:

After much back and forth about whether setting a single reference to a System.Threading.Timer to null will indeed result stopping it have shown that

  1. there are no lingering references, eg events list, as a threading timer takes a sinlge callback and does not expose events.
  2. that if GC collects, the finalizer will indeed dispose the TimerBase and stop the timer.

spike

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

The timer callback is not called. Remove GC.Collect() and the callback is called.

Thanks all.

Why would it?

Consider:

  System.Threading.Timer t = ...;
  System.Threading.Timer q = t;
  q = null; // Should this stop it as well?

Setting to null is something you do to a variable, not something you do to an object. The Timer has no way of knowing you set a particular variable to null, and cannot take action on the basis of that.

EDIT:

To address the edit, even in the case of having a sole reference, it is not guaranteed that the Timer will be stopped, as it is possible the GC may not run after the reference has been set to null. This is not entirely unlikely either, the Microsoft .NET implementation uses a generational collector and a static field will likely survive a nursery collection and be promoted to an older generation. If your program has a relatively stable memory profile there may never be a collection of the older generations (and by extension the finalizer will not run until the end of the program).

I know you're asking about the System.Threading.Timer class, but I want to point out something rather important.

The answers provided so far are good. Logan and SLaks are right that setting any variable to null has no direct effect on the object to which the variable was previously assigned. Russell is right that when the garbage collector does eventually dispose of the timer, however, it will stop.

SLaks indicated that after setting a timer reference to null , there may be lingering references. In the simple example of one System.Threading.Timer reference, this is not the case.

But , if you have, for example, a System.Timers.Timer , and you handle its Elapsed event, then setting it to null will leave a reference and the timer will continue to run forever.

So consider this code for example:

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

In the example above, as there is code holding onto a reference to t in order to handle its Elapsed event, the timer will never stop.

Again, I realize this is not the class you asked about; I only bring this up to point out that it is not always obvious whether in fact you do or do not have any more references to a given object.


UPDATE : It seems some confusion has sprung up on the topic of whether the statement I made above is equally true of a System.Threading.Timer object; it is not . To verify this, consider the following modification of the above code:

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

Here's why it doesn't work for System.Timers.Timer (or any type that has events, actually) :

The answer is very easy to miss when we've defined our event handler like this:

t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);

What if I defined my handler like this instead?

t.Elapsed += (sender, e) => Console.WriteLine(sender.GetType());

Oh right! That sender argument that nobody ever pays any attention to!

The event-handling infrastructure provided by .NET requires that objects handling events maintain references to objects raising events. Otherwise, the contract provided by the very signature of the EventHandler delegate and all its cousins would be violated.

The moral of this story is: as soon as you add a handler to an event, you have created a new reference to an object. The only way to allow that object to be garbage collected after this point is to remove the handler -- but this may be quite difficult if you've set the only other reference to said object to null (this is one of the very few examples of a case where a .NET program may have a "memory leak").

Not necessarily. Setting it to null, removes any references to it, and relies on the garbage collector to dispose of it.

If the timer went off before the GC got to it, it would trigger the event.

No, it will not stop.

Setting a variable to null does not directly have any side-effects such as stopping a timer. (Unless it's a property)

Since the timer has other references, the GC will not collect it, and it will never stop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM