본문 바로가기
Framework/.Net Framework

[C#/Winform] Timer 클래스를 사용하는 방법

by 잘 까먹는 다람쥐 2025. 3. 25.

주기적으로 특정 행위를 반복하는 배치 프로그램이나 에이전트 프로그램을 구현할 때,

.NET의 Timer 클래스를 사용하면 편리하게 구현할 수 있다.

 

하지만 .NET에는 세 가지 Timer 클래스가 존재한다.

  1. System.Windows.Forms.Timer
  2. System.Timers.Timer
  3. System.Threading.Timer

각각 사용 방법도 다르고, 동작하는 개념의 차이도 있다.

 

만약 Timer 객체를 사용하고자 할 때는, 동작 원리를 이해하고 상황에 맞추어 선택할 수 있어야 한다.


1. Timer 클래스의 종류 및 동작 원리

1.1 System.Windows.Forms.Timer

  • WinForms UI 스레드에서 실행되는 타이머로, 주로 UI 업데이트가 필요한 경우 사용
  • UI스레드에서 실행되므로, UI 컨트롤을 간단하게 조작 가능
  • Tick 이벤트 기반으로 동작
  • 정확성이 상대적으로 낮아 배치 작업보다는 대개 UI 관련 이벤트 처리에 사용

정확성이 낮은 이유는 아래와 같다.

Winform은 기본적으로, UI 스레드만을 가진 싱글 스레드 구조
> 만약 UI 이벤트 처리에 지연이 발생하면 더불어 Tick 이벤트 수행도 지연됨
> 반대로, Tick 이벤트에서 긴 작업을 수행하는 경우 UI가 멈출 수 있다.

>> 결과적으로, 안정적인 동작을 보장할 수 없다..! (Thread-safe 하지 않다)

 

예제 코드로 사용 방법을 살펴보자.

 

winform 의 Timer 클래스는 도구 상자로부터 손쉽게 생성할 수 있다.

 

Timer 객체를 생성했다면, 반복할 동작과 옵션을 설정한다.

using System.Diagnostics.Metrics;

namespace TesterWinform
{
    public partial class Form1 : Form
    {
        int counter = 0;

        public Form1()
        {
            InitializeComponent();
            WinformTimertest();
        }

        private void WinformTimertest()
        {
            // 간격을 1초(1000밀리초)로 설정
            timer1.Interval = 1000;

            // Tick 이벤트 핸들러 추가
            timer1.Tick += new EventHandler(Timer_Tick);

            // Timer 시작
            timer1.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            counter++;
            this.Text = $"타이머 실행 중: {counter}초";

            if (counter >= 10)
            {
                timer1.Stop();
                MessageBox.Show("타이머가 10초 후 정지되었습니다.");
            }
        }
    }
}

 

WinformTimerTest() 를 보면,,

위 코드는 '도구상자' 에서 사전에 클래스를 선언했다는 것을 기준이다.

 

만약 코드에서 객체를 생성하고 싶다면

timer1  = new System.Windows.Fomrs.Timer(); 로 생성하면 될 것이다.


 

1.2) System.Threading.Timer

주요 특징은 아래 두 가지이다.

ThreadPool 기반 : CLR의 스레드풀에서 자동적으로 관리된다.
백그라운드 실행 : UI 스레드와 완전히 분리된 스레드에서 실행

 

때문에, UI 작업과 분리되어 멀티쓰레딩 과 같은 작업은 가능해지지만,

만약 UI 작업을 하는 경우 기존 UI 스레드와의 충돌 가능성이 생기게 된다. (이 현상을 Cross-Thread 문제 라고 부른다.)

 

C# 에서는 이러한 상황을 방지하기 위해서, Invoke 라는 함수를 제공하고 있다.

만약 백그라운드 스레드에서 UI 작업을 하고자 한다면, Invoke 함수를 이용하여 신호를 받고 기다린 후 수행하는 것이다.

 

뿐만 아니라, 해당 타이머는 지역 변수로 선언되는 경우, C# 의 GC (가비지 컬렉터)에 의해서 수거될 수 있다.

때문에 지속적인 작업을 부여하는 경우에는 '클래스 필드'로 선언해야 안정적이다.

+ Dispose() 로 반드시 수동 해제를 해야 메모리 누수를 막는다.

 

어떻게 사용하는 지는 아래 코드로 살펴본다.

using System;
using System.Threading;
using System.Windows.Forms;

public class MainForm : Form
{
    private System.Threading.Timer _timer;
    private Label statusLabel;
    private int counter = 0;

    public MainForm()
    {
        InitializeComponents();
        InitializeTimer();
    }

    private void InitializeComponents()
    {
        statusLabel = new Label
        {
            Text = "카운트: 0",
            Location = new System.Drawing.Point(10, 10),
            Width = 200
        };
        Controls.Add(statusLabel);
    }

    private void InitializeTimer()
    {
        _timer = new System.Threading.Timer(
            callback: TimerCallback,
            state: null,
            dueTime: 0,       // 즉시 시작
            period: 1000);    // 1초 간격
    }

    private void TimerCallback(object state)
    {
        counter++;
        
        // UI 스레드로 마샬링 (크로스 스레드 방지)
        if (statusLabel.InvokeRequired)
        {
            statusLabel.BeginInvoke((MethodInvoker)delegate 
            {
                statusLabel.Text = $"카운트: {counter}";
            });
        }
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        _timer?.Dispose();  // 리소스 정리
        base.OnFormClosing(e);
    }
    
}

1.2) System.Timers.Timer

 

주요 특징은 아래와 같다.

- AutoReset = false이면 한 번만 실행됨
- 이벤트 기반 (Elapsed 이벤트)
- 백그라운드에서 실행되지만, SynchronizingObject 설정 시 UI 스레드에서 실행 가능 (크로스 스레드 문제 해결!)

 

사실 기능은 Threading.Timer 와 유사하다.

그 이유는 사실  Win32 API의 TimerQueueTimer 를 기반으로 동작하기 때문이다.

하지만, Threading.Timer 와는 다르게 부가적인 기능이 많으므로 필요한 경우 선택하도록 한다.

 

아래 코드로 사용 방법을 살펴본다.

using System;
using System.Timers;
using System.Windows.Forms;

public class MainForm : Form
{
    private System.Timers.Timer _timer;
    private Label statusLabel;
    private int counter = 0;

    public MainForm()
    {
        InitializeComponents();
        InitializeTimer();
    }

    private void InitializeComponents()
    {
        statusLabel = new Label
        {
            Text = "카운트: 0",
            Location = new System.Drawing.Point(10, 10),
            Width = 200
        };
        Controls.Add(statusLabel);
    }

    private void InitializeTimer()
    {
        // 타이머 인스턴스 생성 (1000ms 주기로 실행)
        _timer = new System.Timers.Timer(1000);
        _timer.Elapsed += TimerElapsed;
        _timer.AutoReset = true; // 반복 실행
        // UI 스레드에서 이벤트 핸들링을 원할 경우, SynchronizingObject를 지정
        _timer.SynchronizingObject = this;
        _timer.Start();
    }

    private void TimerElapsed(object sender, ElapsedEventArgs e)
    {
        counter++;
        // SynchronizingObject가 지정되어 있으면 UI 스레드에서 호출된다.
        statusLabel.Text = $"카운트: {counter}";
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        _timer?.Stop();
        _timer?.Dispose();
        base.OnFormClosing(e);
    }   
}

 

 

 

Threading.Timer 와 Timers.Timer 를 한 눈에 비교해보자.

 

항목
System.Threading.Timer System.Timers.Timer
사용 방식 콜백 메서드(callback)를 사용하여 실행 Elapsed 이벤트를 통해 이벤트 기반으로 동작
스레드 관리 CLR의 스레드풀에서 동작하며, UI 스레드와 완전히 분리됨 기본적으로 스레드풀에서 동작하지만, SynchronizingObject를 설정하면 UI 스레드와 동기화 가능
크로스 스레드 처리 UI 작업 시 Invoke 또는 BeginInvoke를 통해 크로스 스레드 이슈 해결 필요 SynchronizingObject를 지정하면 자동으로 UI 스레드에서 처리
재시작 기능 period 값을 지정하면 반복 실행 AutoReset 프로퍼티를 통해 반복 여부를 설정
가비지 컬렉션 문제 지역 변수로 선언 시 가비지 컬렉터에 의해 수거될 수 있음 마찬가지로, 참조를 유지하지 않으면 수거될 수 있으므로, 필요시 클래스 필드로 선언
리소스 해제 Dispose() 메서드를 명시적으로 호출해야 함 Dispose() 메서드를 호출하여 리소스 해제를 수행해야 함

 

 


 

2. Timer 선택 기준

Timer 클래스 UI 스레드  백그라운드 스레드 실행정확도 사용 예
System.Windows.Forms.Timer O X 낮음 Windows Forms UI 업데이트
System.Timers.Timer X O 중간 주기적인 백그라운드 작업
System.Threading.Timer X O 높음 고성능 타이머, 비동기 작업

 


3. REF

Timer 클래스 (System.Timers) | Microsoft Learn

 

Timer 클래스 (System.Timers)

반복 이벤트를 생성하는 옵션으로 설정된 간격 후 이벤트를 생성합니다.

learn.microsoft.com

C# Timer 차이점

 

C# Timer 차이점

원작자 : 소년포비 특정 작업을 주기적으로 실행하기 위해 흔히 Timer 객체를 사용합니다정해진 시간 간격으로 변수를 업데이트 한다던지, 모니터링 한다던지, 로그를 기록 한다던지, 그 작업 내

sourcenote.tistory.com

.NET Framework: 2019. C# - .NET에서 제공하는 3가지 Timer 비교