If you are learning C# or building a .NET application, you will eventually need code that runs later, repeats every few seconds, checks a service, updates a UI, or schedules background work. That is where timers come in. Understanding how to use a timer in C# helps you build periodic tasks, delayed actions, polling loops, UI updates, and backend maintenance jobs without blocking the main thread.
The confusing part is that C# and .NET have several timer options. A console application timer may use System.Threading.Timer, a service might use System.Timers.Timer, a Windows Forms app usually needs System.Windows.Forms.Timer, and modern async code may work better with PeriodicTimer or Task.Delay.
This guide explains the main timer types in C#, when to use each one, and how to write practical examples. You will see beginner-friendly code for interval-based execution, one-time delayed execution, timer callback functions, async timer patterns, and Windows Forms timers. You can follow along in Visual Studio, JetBrains Rider, the dotnet CLI, or a C# Interactive REPL.
For related fundamentals, see /csharp-basics, /dotnet-tutorials, and /async-programming-guide.
| Direct Answer Box: You use a timer in C# by choosing the right timer type, setting an interval or delay, and attaching a callback method or event handler that runs when the timer fires. For backend or console apps, use System.Threading.Timer, System.Timers.Timer, or PeriodicTimer; for Windows Forms, use System.Windows.Forms.Timer; for simple async delays, use Task.Delay. |
A timer lets your program run code after time passes. That may mean “run this once after 5 seconds” or “run this every 10 seconds until the app stops.” In C#, this is useful for polling APIs, refreshing dashboards, cleaning temporary files, checking job status, saving drafts, or updating a clock on a form.
The basic formula is:
Timer Type (System.Timers / System.Threading / Windows Forms Timer) + Interval + Callback Method = Scheduled Execution in C#
The timer type controls where and how the code runs. The interval controls when it runs. The callback method contains the work.
Microsoft’s .NET documentation explains that .NET provides multiple timer classes for different environments. System.Threading.Timer executes a callback on a ThreadPool thread. System.Timers.Timer raises an elapsed event, usually on a ThreadPool thread. System.Threading.PeriodicTimer supports async waiting for individual ticks. Windows desktop apps may also use System.Windows.Forms.Timer, which runs on the UI thread.
That distinction matters. A timer that works well in a backend service may be wrong for a Windows Forms app. A UI timer should update controls safely on the UI thread. A backend timer should avoid blocking and handle overlapping executions carefully.
Use this quick guide:
| Timer Type | Best For | Runs On |
| System.Timers.Timer | Service-style recurring events | Usually ThreadPool |
| System.Threading.Timer | Lightweight callback-based background work | ThreadPool |
| System.Threading.PeriodicTimer | Modern async periodic loops | Async flow |
| System.Windows.Forms.Timer | Windows Forms UI updates | UI thread |
| Task.Delay | Simple one-time delay or async loop | Async flow |
If you are writing a backend worker, console app, or service, start with PeriodicTimer for async code or System.Threading.Timer for callback-based code. If you are building a form with buttons and labels, use System.Windows.Forms.Timer.
Most timer APIs use milliseconds or TimeSpan. Use TimeSpan when possible because it is easier to read and less error-prone.
// 5 seconds as milliseconds
int interval = 5000;
// 5 seconds as TimeSpan
TimeSpan intervalTime = TimeSpan.FromSeconds(5);
The callback is the method that runs when the timer fires.
static void DoWork()
{
Console.WriteLine($”Timer fired at {DateTime.Now:T}”);
}
Timers often keep running until you stop or dispose them. Always clean them up when your program no longer needs them.
timer.Dispose();
System.Timers.Timer is beginner-friendly because it uses an Elapsed event. It is common in service-style applications and simple periodic tasks.
using System;
using System.Timers;
class Program
{
private static Timer? _timer;
static void Main()
{
_timer = new Timer(2000); // 2 seconds
_timer.Elapsed += OnTimerElapsed;
_timer.AutoReset = true;
_timer.Enabled = true;
Console.WriteLine(“Timer started. Press Enter to stop.”);
Console.ReadLine();
_timer.Stop();
_timer.Dispose();
}
private static void OnTimerElapsed(object? sender, ElapsedEventArgs e)
{
Console.WriteLine($”System.Timers.Timer fired at {e.SignalTime:T}”);
}
}
System.Threading.Timer is lightweight and uses a timer callback function in C#. It is useful when you want a direct callback instead of an event.
using System;
using System.Threading;
class Program
{
private static Timer? _timer;
static void Main()
{
_timer = new Timer(
callback: CheckStatus,
state: null,
dueTime: TimeSpan.FromSeconds(1),
period: TimeSpan.FromSeconds(3));
Console.WriteLine(“Threading timer running. Press Enter to exit.”);
Console.ReadLine();
_timer.Dispose();
}
private static void CheckStatus(object? state)
{
Console.WriteLine($”Checking status at {DateTime.Now:T}”);
}
}
For one-time delay execution, a full timer may be unnecessary. Task.Delay is often the cleanest option.
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine(“Waiting 3 seconds…”);
await Task.Delay(TimeSpan.FromSeconds(3));
Console.WriteLine(“This runs after the delay.”);
}
}
For modern async code, PeriodicTimer is often the cleanest option because it works naturally with await.
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(2));
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
try
{
while (await timer.WaitForNextTickAsync(cts.Token))
{
Console.WriteLine($”Async tick at {DateTime.Now:T}”);
}
}
catch (OperationCanceledException)
{
Console.WriteLine(“Timer cancelled.”);
}
}
}
A Windows Forms timer is designed for UI apps. It runs on the UI thread, so it can safely update controls like labels.
using System;
using System.Windows.Forms;
public partial class MainForm : Form
{
private readonly Timer _timer = new Timer();
public MainForm()
{
InitializeComponent();
_timer.Interval = 1000; // 1 second
_timer.Tick += Timer_Tick;
_timer.Start();
}
private void Timer_Tick(object? sender, EventArgs e)
{
timeLabel.Text = DateTime.Now.ToLongTimeString();
}
}
Use the right timer for the application type. That decision prevents many subtle bugs.
A useful benchmark from Microsoft’s timer documentation: Windows Forms Timer is single-threaded and limited to about 55 milliseconds of accuracy, while System.Threading.Timer and System.Timers.Timer use system clock resolution. In practice, timers are not precision schedulers. They are good for application-level intervals, not hard real-time execution.
One common mistake is letting the timer be garbage collected. If you create a System.Threading.Timer inside a method and do not keep a reference to it, the runtime may collect it. Store it in a field when it needs to stay active.
Another mistake is assuming timer callbacks never overlap. If the callback takes longer than the interval, another callback may begin before the first one finishes. This can cause duplicate work, race conditions, and corrupted shared state.
private static int _isRunning = 0;
private static void DoWork(object? state)
{
if (Interlocked.Exchange(ref _isRunning, 1) == 1)
return;
try
{
Console.WriteLine(“Running scheduled work…”);
Thread.Sleep(5000);
}
finally
{
Interlocked.Exchange(ref _isRunning, 0);
}
}
Do not block UI timers with long-running work. A Windows Forms timer runs on the UI thread, so slow code can freeze the interface. Move heavy work to a background task and update the UI when complete.
Do not use timers as a replacement for durable job scheduling. For serious scheduling tasks in C#, especially jobs that must survive app restarts, use a scheduler, hosted service, queue, database-backed job system, or cloud scheduler.
Finally, always handle exceptions. Timer exceptions can behave differently depending on the timer type and whether async code is involved. Log failures and design your callback so one bad run does not silently break the application.
Use the TIMER Framework to choose and implement timers correctly:
Choose the timer based on the application: console/backend, Windows Forms, or simple async delay.
Create the timer, set its interval, and connect the callback or event handler.
Decide whether the work should run once, repeatedly, or until cancellation.
Know where the code runs. ThreadPool timers are not UI-safe. UI timers are not good for heavy work.
Think about drift, overlap, disposal, app shutdown, and error handling.
Use this checklist before shipping:
Learning how to use a timer in C# is really about choosing the right timing tool for the job. Use Task.Delay for simple delayed execution, System.Timers.Timer for event-based periodic tasks, System.Threading.Timer for lightweight callback-based work, PeriodicTimer for modern async loops, and System.Windows.Forms.Timer for Windows Forms UI updates.
The safest pattern is to start small: create a console application timer, log a message every few seconds, add cancellation, then test what happens when the callback takes longer than the interval. That small project will teach you more than memorizing timer classes.
Next step: build a mini timer-based project, such as a console heartbeat logger, a simple reminder app, or a background health-check worker using the .NET SDK, Visual Studio, or the dotnet CLI.
The best timer depends on the application. Use PeriodicTimer for modern async code, System.Threading.Timer for lightweight callbacks, System.Timers.Timer for event-based server tasks, and System.Windows.Forms.Timer for Windows Forms UI updates. There is no single best timer for every project.
Create a timer with a 5-second interval and place your logic in the callback or event handler. In modern async code, PeriodicTimer with TimeSpan.FromSeconds(5) is clean and readable. For callback-based code, System.Threading.Timer also works well.
Use await Task.Delay(TimeSpan.FromSeconds(3)) when you simply want to wait before continuing. This is usually better than creating a timer for one delayed action. In synchronous code, avoid blocking with Thread.Sleep unless you intentionally want to block the current thread.
System.Timers.Timer raises an Elapsed event, while System.Threading.Timer executes a callback delegate. Both are commonly used in backend or service-style code and may run on ThreadPool threads. System.Timers.Timer can feel easier for beginners because it uses events.
Yes. A console application timer can use System.Threading.Timer, System.Timers.Timer, or PeriodicTimer. Make sure the console app stays alive while the timer runs, usually with Console.ReadLine(), an async loop, or a hosted application pattern.
C# timers are good for application-level intervals but not hard real-time scheduling. They can be affected by ThreadPool load, callback duration, system clock resolution, and app shutdown. For durable production scheduling, consider a hosted service, queue, database-backed job scheduler, or cloud scheduler.
Microsoft Learn: System.Threading.Timer
Microsoft Learn: System.Timers.Timer
Microsoft Learn: System.Windows.Forms.Timer