154

Just this - How do you add a timer to a C# console application? It would be great if you could supply some example coding.

Johan Bresler
  • 6,450
  • 11
  • 56
  • 77
  • 20
    Caution: the answers here have a bug, the Timer object is going to get garbage collected. The reference to the timer must be stored in a static variable to ensure it keeps ticking. – Hans Passant Dec 14 '14 at 21:45
  • 2
    @HansPassant You seem to have missed the clear statement in my answer: "It is also recommended to always use a static (shared in VB.NET) System.Threading.Timer if you are developing a Windows Service and require a timer to run periodically. This will avoid possibly premature garbage collection of your timer object." If people want to copy a random example and use it blindly that's their problem. – Ash Sep 02 '19 at 12:18

12 Answers12

148

That's very nice, however in order to simulate some time passing we need to run a command that takes some time and that's very clear in second example.

However, the style of using a for loop to do some functionality forever takes a lot of device resources and instead we can use the Garbage Collector to do some thing like that.

We can see this modification in the code from the same book CLR Via C# Third Ed.

using System;
using System.Threading;

public static class Program 
{
   private Timer _timer = null;
   public static void Main() 
   {
      // Create a Timer object that knows to call our TimerCallback
      // method once every 2000 milliseconds.
      _timer = new Timer(TimerCallback, null, 0, 2000);
      // Wait for the user to hit <Enter>
      Console.ReadLine();
   }

   private static void TimerCallback(Object o) 
   {
      // Display the date/time when this method got called.
      Console.WriteLine("In TimerCallback: " + DateTime.Now);
   }
}
Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
Khalid Al Hajami
  • 1,536
  • 1
  • 10
  • 2
  • 3
    Khalid, this was extremely helpful. Thanks. The console.readline() and the GC.Collect was just what I needed. – Seth Spearman Dec 21 '12 at 16:35
  • I had my Console.ReadLine commented out so the program terminated itself after the first callback. Thanks @SethSpearman – Dumisani Aug 12 '14 at 08:52
  • 9
    @Ralph Willgoss, Why GC.Collect(); is required? – Puchacz Feb 17 '15 at 18:25
  • 2
    @Puchacz I don't see a point of calling `GC.Collect()`. There is nothing to collect. It would make sense, if `GC.KeepAlive(t)` was called after `Console.ReadLine();` – newprint Oct 20 '15 at 20:22
  • 2
    It terminated after first callback – BadPiggie Sep 18 '18 at 07:11
  • @Khalid Al Hajami You seem to have simply copied my answer and added a pointless GC.Collect() to differentiate. All this 3 years later too. – Ash Sep 02 '19 at 12:14
  • 2
    @Khalid Al Hajami "However, the style of using a for loop to do some functionality forever takes a lot of device resources and instead we can use the Garbage Collector to do some thing like that." This is absolute nonsensical rubbish. The garbage collector is utterly irrelevant. Did you copy this from a book and not understand what you were copying? – Ash Sep 02 '19 at 12:24
  • @Khalid Al Hajami What is the point of calling `GC.Collect()`? I don't see the point. Readers are warned of this answer before it is clarified. – Pingpong May 23 '20 at 04:22
  • The `_timer` variable needs to be `static` for it to compile. – Jan Nov 01 '21 at 12:07
77

Use the System.Threading.Timer class.

System.Windows.Forms.Timer is designed primarily for use in a single thread usually the Windows Forms UI thread.

There is also a System.Timers class added early on in the development of the .NET framework. However it is generally recommended to use the System.Threading.Timer class instead as this is just a wrapper around System.Threading.Timer anyway.

It is also recommended to always use a static (shared in VB.NET) System.Threading.Timer if you are developing a Windows Service and require a timer to run periodically. This will avoid possibly premature garbage collection of your timer object.

Here's an example of a timer in a console application:

using System; 
using System.Threading; 
public static class Program 
{ 
    public static void Main() 
    { 
       Console.WriteLine("Main thread: starting a timer"); 
       Timer t = new Timer(ComputeBoundOp, 5, 0, 2000); 
       Console.WriteLine("Main thread: Doing other work here...");
       Thread.Sleep(10000); // Simulating other work (10 seconds)
       t.Dispose(); // Cancel the timer now
    }
    // This method's signature must match the TimerCallback delegate
    private static void ComputeBoundOp(Object state) 
    { 
       // This method is executed by a thread pool thread 
       Console.WriteLine("In ComputeBoundOp: state={0}", state); 
       Thread.Sleep(1000); // Simulates other work (1 second)
       // When this method returns, the thread goes back 
       // to the pool and waits for another task 
    }
}

From the book CLR Via C# by Jeff Richter. By the way this book describes the rationale behind the 3 types of timers in Chapter 23, highly recommended.

Cᴏʀʏ
  • 105,112
  • 20
  • 162
  • 194
Ash
  • 60,973
  • 31
  • 151
  • 169
  • Can you supply a little more information on the actual coding? – Johan Bresler Oct 09 '08 at 06:12
  • Does the example from msdn work for you? http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx – Eric Tuttleman Oct 09 '08 at 06:15
  • Eric, I haven't tried it but would not be unusual if there was a problem with it. I notice it is also trying to do some sort of inter-thread synchronisation, this is alsways an area that can be tricky to get right. If you can avoid it in your design, it is always smart to do so. – Ash Oct 09 '08 at 06:29
  • 1
    Ash - I definitely agree about msdn examples. I wouldn't immediately discount the synchronization code though, if the timmer runs in it's own thread, then you are writing a multi-threaded app and need to be aware of issues relating to synchronization. – Eric Tuttleman Oct 09 '08 at 09:13
  • 1
    What happens if there are multiple methods that match the TimerCallback delegate signature? – Ozkan Jun 06 '18 at 15:54
  • @Ozcan i think you can't have two methods with same name and signature – beppe9000 Sep 29 '19 at 10:52
29

Here is the code to create a simple one second timer tick:

  using System;
  using System.Threading;

  class TimerExample
  {
      static public void Tick(Object stateInfo)
      {
          Console.WriteLine("Tick: {0}", DateTime.Now.ToString("h:mm:ss"));
      }

      static void Main()
      {
          TimerCallback callback = new TimerCallback(Tick);

          Console.WriteLine("Creating timer: {0}\n", 
                             DateTime.Now.ToString("h:mm:ss"));

          // create a one second timer tick
          Timer stateTimer = new Timer(callback, null, 0, 1000);

          // loop here forever
          for (; ; )
          {
              // add a sleep for 100 mSec to reduce CPU usage
              Thread.Sleep(100);
          }
      }
  }

And here is the resulting output:

    c:\temp>timer.exe
    Creating timer: 5:22:40

    Tick: 5:22:40
    Tick: 5:22:41
    Tick: 5:22:42
    Tick: 5:22:43
    Tick: 5:22:44
    Tick: 5:22:45
    Tick: 5:22:46
    Tick: 5:22:47

EDIT: It is never a good idea to add hard spin loops into code as they consume CPU cycles for no gain. In this case that loop was added just to stop the application from closing, allowing the actions of the thread to be observed. But for the sake of correctness and to reduce the CPU usage a simple Sleep call was added to that loop.

jussij
  • 10,370
  • 1
  • 33
  • 49
  • 10
    The for (; ; ) { } causes 100% cpu usage. – Seth Spearman Dec 21 '12 at 16:36
  • 1
    Isn't it fairly obvious if you have an infinite for loop then that will result in a CPU of 100%. To fix that all you need to do is add a sleep call to the loop. – veight Aug 16 '13 at 14:52
  • Isn't `while(1)` better? I have never seen anyone using `for (; ; ) { }` for infinite loops but always `while(1)`. – Azimuth Feb 17 '14 at 07:45
  • 3
    It is amazing how many people are fixated on whether the for loop should be a while loop and why the CPU goes to 100%. Talk about miss the wood for the trees! Azimuth, I would personally like to know how a while(1) would be any different to the infinite for loop? Surely the people that write the CLR compiler optimiser will make sure these two code constructs create the exact same CLR code? – Blake7 Feb 17 '14 at 14:05
  • 1
    One reason why while(1) will not work is it is not valid c#: test.cs(21,20): error CS0031: Constant value '1' cannot be converted to a 'bool' – Blake7 Feb 17 '14 at 14:07
  • `for (; ; ) { }` looks like half a fork bomb -It just needs to create anonymous functions in there and boom, Stack Overflow. – Piotr Kula Mar 26 '14 at 17:03
  • 1
    Not on my machine (win8.1, i5), only about 20-30%, what kind of computer did you have back then? @SethSpearman – shinzou Apr 22 '16 at 17:59
17

Lets Have A little Fun

using System;
using System.Timers;

namespace TimerExample
{
    class Program
    {
        static Timer timer = new Timer(1000);
        static int i = 10;

        static void Main(string[] args)
        {            
            timer.Elapsed+=timer_Elapsed;
            timer.Start(); Console.Read();
        }

        private static void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            i--;

            Console.Clear();
            Console.WriteLine("=================================================");
            Console.WriteLine("                  DEFUSE THE BOMB");
            Console.WriteLine(""); 
            Console.WriteLine("                Time Remaining:  " + i.ToString());
            Console.WriteLine("");        
            Console.WriteLine("=================================================");

            if (i == 0) 
            {
                Console.Clear();
                Console.WriteLine("");
                Console.WriteLine("==============================================");
                Console.WriteLine("         B O O O O O M M M M M ! ! ! !");
                Console.WriteLine("");
                Console.WriteLine("               G A M E  O V E R");
                Console.WriteLine("==============================================");

                timer.Close();
                timer.Dispose();
            }

            GC.Collect();
        }
    }
}
beppe9000
  • 1,056
  • 1
  • 13
  • 28
Real Caz
  • 196
  • 1
  • 4
10

Or using Rx, short and sweet:

static void Main()
{
Observable.Interval(TimeSpan.FromSeconds(10)).Subscribe(t => Console.WriteLine("I am called... {0}", t));

for (; ; ) { }
}
Yonatan Zetuny
  • 129
  • 1
  • 4
4

You can also use your own timing mechanisms if you want a little more control, but possibly less accuracy and more code/complexity, but I would still recommend a timer. Use this though if you need to have control over the actual timing thread:

private void ThreadLoop(object callback)
{
    while(true)
    {
        ((Delegate) callback).DynamicInvoke(null);
        Thread.Sleep(5000);
    }
}

would be your timing thread(modify this to stop when reqiuired, and at whatever time interval you want).

and to use/start you can do:

Thread t = new Thread(new ParameterizedThreadStart(ThreadLoop));

t.Start((Action)CallBack);

Callback is your void parameterless method that you want called at each interval. For example:

private void CallBack()
{
    //Do Something.
}
mattlant
  • 15,384
  • 4
  • 34
  • 44
3

In C# 5.0+ and .NET Framework 4.5+ you can use async/await:

async void RunMethodEvery(Action method, double seconds)
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromSeconds(seconds));
        method();
    }
 }
Ayub
  • 2,345
  • 27
  • 29
2

You can also create your own (if unhappy with the options available).

Creating your own Timer implementation is pretty basic stuff.

This is an example for an application that needed COM object access on the same thread as the rest of my codebase.

/// <summary>
/// Internal timer for window.setTimeout() and window.setInterval().
/// This is to ensure that async calls always run on the same thread.
/// </summary>
public class Timer : IDisposable {

    public void Tick()
    {
        if (Enabled && Environment.TickCount >= nextTick)
        {
            Callback.Invoke(this, null);
            nextTick = Environment.TickCount + Interval;
        }
    }

    private int nextTick = 0;

    public void Start()
    {
        this.Enabled = true;
        Interval = interval;
    }

    public void Stop()
    {
        this.Enabled = false;
    }

    public event EventHandler Callback;

    public bool Enabled = false;

    private int interval = 1000;

    public int Interval
    {
        get { return interval; }
        set { interval = value; nextTick = Environment.TickCount + interval; }
    }

    public void Dispose()
    {
        this.Callback = null;
        this.Stop();
    }

}

You can add events as follows:

Timer timer = new Timer();
timer.Callback += delegate
{
    if (once) { timer.Enabled = false; }
    Callback.execute(callbackId, args);
};
timer.Enabled = true;
timer.Interval = ms;
timer.Start();
Window.timers.Add(Environment.TickCount, timer);

To make sure the timer works you need to create an endless loop as follows:

while (true) {
     // Create a new list in case a new timer
     // is added/removed during a callback.
     foreach (Timer timer in new List<Timer>(timers.Values))
     {
         timer.Tick();
     }
}
Steven de Salas
  • 20,944
  • 9
  • 74
  • 82
1

Use the PowerConsole project on Github at https://github.com/bigabdoul/PowerConsole or the equivalent NuGet package at https://www.nuget.org/packages/PowerConsole. It elegantly handles timers in a reusable fashion. Take a look at this sample code:

using PowerConsole;

namespace PowerConsoleTest
{
    class Program
    {
        static readonly SmartConsole MyConsole = SmartConsole.Default;

        static void Main()
        {
            RunTimers();
        }

        public static void RunTimers()
        {
            // CAUTION: SmartConsole is not thread safe!
            // Spawn multiple timers carefully when accessing
            // simultaneously members of the SmartConsole class.

            MyConsole.WriteInfo("\nWelcome to the Timers demo!\n")

            // SetTimeout is called only once after the provided delay and
            // is automatically removed by the TimerManager class
            .SetTimeout(e =>
            {
                // this action is called back after 5.5 seconds; the name
                // of the timer is useful should we want to clear it
                // before this action gets executed
                e.Console.Write("\n").WriteError("Time out occured after 5.5 seconds! " +
                    "Timer has been automatically disposed.\n");

                // the next statement will make the current instance of 
                // SmartConsole throw an exception on the next prompt attempt
                // e.Console.CancelRequested = true;

                // use 5500 or any other value not multiple of 1000 to 
                // reduce write collision risk with the next timer
            }, millisecondsDelay: 5500, name: "SampleTimeout")

            .SetInterval(e =>
            {
                if (e.Ticks == 1)
                {
                    e.Console.WriteLine();
                }

                e.Console.Write($"\rFirst timer tick: ", System.ConsoleColor.White)
                .WriteInfo(e.TicksToSecondsElapsed());

                if (e.Ticks > 4)
                {
                    // we could remove the previous timeout:
                    // e.Console.ClearTimeout("SampleTimeout");
                }

            }, millisecondsInterval: 1000, "EverySecond")

            // we can add as many timers as we want (or the computer's resources permit)
            .SetInterval(e =>
            {
                if (e.Ticks == 1 || e.Ticks == 3) // 1.5 or 4.5 seconds to avoid write collision
                {
                    e.Console.WriteSuccess("\nSecond timer is active...\n");
                }
                else if (e.Ticks == 5)
                {
                    e.Console.WriteWarning("\nSecond timer is disposing...\n");

                    // doesn't dispose the timer
                    // e.Timer.Stop();

                    // clean up if we no longer need it
                    e.DisposeTimer();
                }
                else
                {
                    System.Diagnostics.Trace.WriteLine($"Second timer tick: {e.Ticks}");
                }
            }, 1500)
            .Prompt("\nPress Enter to stop the timers: ")
            
            // makes sure that any remaining timer is disposed off
            .ClearTimers()

            .WriteSuccess("Timers cleared!\n");
        }
    }
}
Bigabdoul
  • 721
  • 6
  • 12
0

doc

There you have it :)

public static void Main()
   {
      SetTimer();

      Console.WriteLine("\nPress the Enter key to exit the application...\n");
      Console.WriteLine("The application started at {0:HH:mm:ss.fff}", DateTime.Now);
      Console.ReadLine();
      aTimer.Stop();
      aTimer.Dispose();

      Console.WriteLine("Terminating the application...");
   }

   private static void SetTimer()
   {
        // Create a timer with a two second interval.
        aTimer = new System.Timers.Timer(2000);
        // Hook up the Elapsed event for the timer. 
        aTimer.Elapsed += OnTimedEvent;
        aTimer.AutoReset = true;
        aTimer.Enabled = true;
    }

    private static void OnTimedEvent(Object source, ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0:HH:mm:ss.fff}",
                          e.SignalTime);
    }
Omar AMEZOUG
  • 958
  • 2
  • 14
  • 38
XvXLuka222
  • 131
  • 6
0

I suggest you following Microsoft guidelines ( https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=netcore-3.1).

I first tried using System.Threading; with

var myTimer = new Timer((e) =>
{
   // Code
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

but it continuously stopped after ~20 minutes.

With that, I tried the solutions setting

GC.KeepAlive(myTimer)

or

for (; ; ) { }
}

but they didn't work in my case.

Following Microsoft documentation, it worked perfectly:

using System;
using System.Timers;

public class Example
{
    private static Timer aTimer;

    public static void Main()
    {
        // Create a timer and set a two second interval.
        aTimer = new System.Timers.Timer();
        aTimer.Interval = 2000;

        // Hook up the Elapsed event for the timer. 
        aTimer.Elapsed += OnTimedEvent;

        // Have the timer fire repeated events (true is the default)
        aTimer.AutoReset = true;

        // Start the timer
        aTimer.Enabled = true;

        Console.WriteLine("Press the Enter key to exit the program at any time... ");
        Console.ReadLine();
    }

    private static void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    }
}
// The example displays output like the following: 
//       Press the Enter key to exit the program at any time... 
//       The Elapsed event was raised at 5/20/2015 8:48:58 PM 
//       The Elapsed event was raised at 5/20/2015 8:49:00 PM 
//       The Elapsed event was raised at 5/20/2015 8:49:02 PM 
//       The Elapsed event was raised at 5/20/2015 8:49:04 PM 
//       The Elapsed event was raised at 5/20/2015 8:49:06 PM 
0

you can use StopWatch class, here's an example

StopWatch stopwatch = new Stopwatch();
// creating a new stopwatch class
stopwatch.Start();
// starting the stopwatch
Thread.Sleep(10000);
// waiting for 10 seconds

TimeSpan timespan = stopwatch.Elapsed;
/* creating a new timespan class and concacting it with the elapsed of the 
stopwatch class */
string time = String.Format("{0:00}:{1:00}:{2:00}",
timespan.Hours, timespan.Minutes, timespan.Seconds
);

Console.Write($"The time right now is {time}");

Console.ReadKey();
Saamer
  • 4,687
  • 1
  • 13
  • 55