q0987
q0987

Reputation: 35982

C# -- When will the instance of Timer be killed?

Given the following code, the timer will trigger the PrintTimer function every one second until the user presses a key.

Question 1> When the Timer will be stopped? Is the Timer by default a Background thread so it will be killed after the foreground thread is terminated.

Question 2> How to programmatically stop the timer in the calling thread?

class Program
{
    // continue to call this function until the user presses a 
    // key to terminate the application
    static void PrintTime(object state) 
    {
        Console.WriteLine("Time is: {0}",
        DateTime.Now.ToLongTimeString());
    }
    static void Main(string[] args)
    {
        TimerCallback timeCB = new TimerCallback(PrintTime);
        System.Threading.Timer t = new Timer(
            timeCB,
            null,
            0,
            1000);
        Console.WriteLine("Hit key to terminate...");
        Console.ReadLine();
    }
}

Upvotes: 0

Views: 2709

Answers (5)

Frank Boyne
Frank Boyne

Reputation: 4570

For Question 2 see the Timer.Enabled property and the Timer.Close() method

Update: I suppose for completeness I should also have mentioned the Timer.Stop() method (which sets Enabled to false)

Update: When should each option be used?

Broadly speaking there are two situations: either you are getting rid of the timer and never plan to use it again or else you are pausing the timer but plan to revive it at some point (or at least want the option to be able to revive it).

If you are getting rid of the timer then Dispose/Close is the route to take. At minimum you should call Dispose because that's what a well behaved program is expected to do when it is finished with an object that implements IDisposable.

That said, when an object implements both IDisposable and a Close method I believe the better approach is to call Close because Close may do more than just free resources. For example it might flush buffers or do some other object specific thing. One would expect a Close method to call Dispose. Ideally the documentation for Close would explicitly state that it calls Dispose (but the documentation for Timer::Close does not say that it does).

To temporarily pause and revive a timer you can set the Enabled property to false and then set it to true again. Alternatively you can call Stop() and then call Start() to revive it. Off hand I don't have a preference for one technique over the other.

I suppose one might argue that Stop and Start are slightly more readable and perhaps slightly less efficient (since they in turn set Enabled).

Upvotes: 1

Maxim
Maxim

Reputation: 7348

In your application it will never stop until you exit your application.

To stop it programatically, it depends on which timer are you using.

System.Timers.Timer

System.Threading.Timer

If the first one, you can call the Stop method. If the second one, you have to Dispose() it.

Please note that if you start the timer in a method and don't keep a reference to it, it will be stopped when it gets garbage collected.

Upvotes: 1

Drew Marsh
Drew Marsh

Reputation: 33379

Answer 1

Right now the timer will be stopped only when the Program exits. It's not really sitting there using a background thread, but when the timer event occurs the callback will be invoked via a ThreadPool thread which is a background thread.

Answer 2

Stopping the timer is as simple as either:

t.Change(Timeout.Infinite, Timeout.Infinite);

-- or --

t.Dispose();

The former allows you to restart the Timer with another call to Change where as the latter will clean up the Timer entirely.

For more detailed information on its behavior, I suggest reading the Remarks section of the Timer class in its MSDN documentation. For instance, there's some tricky timing with Dispose because the Timer may have already queued up a notification callback to the thread pool which you could technically receive even after calling Dispose.

Upvotes: 4

Michael Kennedy
Michael Kennedy

Reputation: 3380

The timer will be cleaned up with its finzalizer runs. That's when it becomes garabage, which actually varies from release to debug mode.

Here's a test we can run using WeakReference:

internal class Program
{
    // continue to call this function until the user presses a 
    // key to terminate the application
    private static void PrintTime(object state)
    {
        Console.WriteLine("Time is: {0}",
                          DateTime.Now.ToLongTimeString());
    }

    private static void Main(string[] args)
    {
        TimerCallback timeCB = new TimerCallback(PrintTime);
        Timer t = new Timer(
            timeCB,
            null,
            0,
            1000);

        WeakReference wk = new WeakReference(t);
        PrintStatus(wk);

        Console.WriteLine("Hit key to terminate...");
        Console.ReadLine();

        t = null;
        PrintStatus(wk);
    }

    private static void PrintStatus(WeakReference wk)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Is timer alive? " + wk.IsAlive);
    }
}

Upvotes: 2

Yaur
Yaur

Reputation: 7452

Timer is disposable so you should/must call Dispose on it. You can also call Stop if you want to reuse it but there is a bug in the Timer class with a periodic callback that makes it advisable to just Dispose it and start with a new timer.

What will happen in the code that you have posted is that the Timer's finalizer will (technically may) be called which will kill it.

Upvotes: 1

Related Questions