우선 Timer 관련 글들을 살펴보면
Forms.Timer 가 Winform 에 '최적화 되었다' 라는 구문이 눈에 띄는데.
이걸 보고 알아서 잘 관리해 주겟거니... 하다가는
함정에 빠지게 된다.
1. Forms.Timer
Forms.Timer 는 멀티쓰레드 방식으로 동작하지 않고 UI Thread 와 자원을 공유하여 사용한다.
때문에 Forms.Timer 를 여러개 만들어 구동시켜도 CPU 점유율은 일정이상 올라가지 않는다.
거기에 더해,
UI Thread 를 공유하기에 Forms.Timer 에서 처리하는 작업이 하드하다면
UI 가 갱신되지 않는 문제점이 발생한다.
아주 간단한 예제로 이 문제점을 확인할 수 있는데,
실행 시켜보면 아마 UI는 먹통이 될것이다.
그리고 CPU 자원 역시
단일코어만 사용하는 모습을 살펴볼 수 있다.
2. Threading.Timer
반면 Threading.Timer 는 '새로운 Thread 를 할당한다'
namespace WinFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
StartTimers();
}
public void StartTimers()
{
timer1 = new System.Threading.Timer(timer1_Tick, null, 0, 100);
timer2 = new System.Threading.Timer(timer2_Tick, null, 0, 100);
timer3 = new System.Threading.Timer(timer3_Tick, null, 0, 100);
}
System.Threading.Timer timer1;
System.Threading.Timer timer2;
System.Threading.Timer timer3;
private void timer1_Tick(object? garbage)
{
ulong sum = 0;
for(ulong i = 0; i < 999999999; ++i)
{
sum += i;
sum /= 2;
}
if(textBox1.InvokeRequired)
{
textBox1.Invoke(new Action(() =>
{
textBox1.Text = sum.ToString();
}));
}
else
{
textBox1.Text = sum.ToString();
}
}
private void timer2_Tick(object? garbage)
{
ulong sum = 0;
for (ulong i = 0; i < 999999999; ++i)
{
sum += i;
sum /= 2;
}
if (textBox2.InvokeRequired)
{
textBox2.Invoke(new Action(() =>
{
textBox2.Text = sum.ToString();
}));
}
else
{
textBox2.Text = sum.ToString();
}
}
private void timer3_Tick(object? garbage)
{
ulong sum = 0;
for (ulong i = 0; i < 999999999; ++i)
{
sum += i;
sum /= 2;
}
if (textBox3.InvokeRequired)
{
textBox3.Invoke(new Action(() =>
{
textBox3.Text = sum.ToString();
}));
}
else
{
textBox3.Text = sum.ToString();
}
}
}
}
위와 같이 간단하게 코드를 수정 해 주면
CPU 사용량은 미쳐 날뛰지만, 놀랍게도 UI Thread 는 멈추지 않는다.
때문에 WinForm 창 크기를 조정 하거나, 타이틀 바를 잡고 마구 흔들어도
전혀 끊김없이 사용이 가능하다.
위 for 문 은 100ms 내에 수행하기엔 벅찬 작업 이므로
미처 작업이 끝마치기 전에 100ms 가 지나 다시 새로운 thread 를 생성해 작업을 돌리게 된다.
중단점을 잡아 Debug 해 보면
Pool 내에 여러 Thread 가 생성되어 있음을 확인할 수 있다.
3. Conclusion
간단한 테스트 용도로는 Forms.Timer 를 사용해도 문제될것이 없지만,
실시간 처리를 해야 한다거나, 여러 Timer 를 운용해야 한다면
반드시 Threading.Timer 를 사용하도록 하자.
여담으로 WPF 에서 기본 Timer 로 사용하는 DispatcherTimer 역시 Forms.Timer 와 동작방식이 비슷하다.
'Programming > C#' 카테고리의 다른 글
[C#] Task, 비동기에 대해 (0) | 2023.08.08 |
---|---|
MAUI의 미래에 대해 (0) | 2023.06.05 |
[C#] SharedMemory 사용법 (0) | 2022.05.12 |
[C#] UDP Multicast 수신 (0) | 2022.04.08 |
[C#]System.Timers.Timer 사용시 Race Condition (0) | 2021.12.22 |