PeriodicTimer / System.Threading.Timer / DispatcherTimer의 사용 구분 - .NET의 정기 실행을 먼저 정리
· 小村 豪 · C#, .NET, WPF, 타이머, 설계
지난번 일반 Windows에서 소프트 리얼타임을 가능한 한 실현하기 위한 실천 가이드 - 먼저 보는 체크리스트에서는 Sleep 맡기기의 주기 루프를 피하고, 이벤트 구동이나 waitable timer를 사용하는 이야기를 정리했습니다.
그럼, 좀 더 일상적인 .NET 앱 개발에서는 어떻게 할 것인가.
여기서 망설이기 쉬운 것이 PeriodicTimer, System.Threading.Timer, DispatcherTimer입니다.
이름은 전부 타이머지만,
await로 틱을 기다리는 타이머- ThreadPool에서 callback이 날아오는 타이머
- UI 스레드의
Dispatcher위에서 동작하는 타이머
라는 식으로 성격이 꽤 다릅니다.
실무에서 섞이기 쉬운 것은 대개 이 부근입니다.
- 비동기 정기 처리인데
System.Threading.Timer에async람다를 넘겨버린다 - WPF의 UI 업데이트인데 ThreadPool 타이머에서 직접 화면을 건드려버린다
DispatcherTimer에 무거운 처리를 넣어서 화면째로 둔화시킨다- 지난번의 「소프트 리얼타임」 이야기와, 일상의 앱의 정기 실행이 머릿속에서 섞인다
이 글에서는 주로 .NET 6 이후의 일반적인 C# / .NET 앱을 전제로,
PeriodicTimer / System.Threading.Timer / DispatcherTimer를 일상의 실무에서 망설이기 어려운 순서로 정리합니다.
대상은 예를 들어 다음과 같은 것입니다.
- worker / 백그라운드 서비스
- 콘솔 앱
- ASP.NET Core의 뒷일 처리
- WPF의 데스크톱 앱
이 글에서 말하는 DispatcherTimer는 주로 WPF의 System.Windows.Threading.DispatcherTimer를 가리킵니다.
WinUI / UWP에도 같은 사고 방식의 DispatcherTimer가 있습니다.
WinForms라면 UI용 타이머로서는 System.Windows.Forms.Timer를 보는 편이 자연스럽습니다.
또한, 여기서 다루는 것은 앱 측의 정기 실행을 어떻게 쓸까입니다. 주기의 정확성 자체가 주제일 때는, 지난번 소프트 리얼타임 기사의 이야기로 돌아갑니다.
1. 먼저 결론(한마디로)
await베이스로 일정 간격의 처리를 자연스럽게 쓰고 싶다면, 먼저PeriodicTimer- ThreadPool 위에서 가벼운 callback을 정기적으로 기동하고 싶다면,
System.Threading.Timer - WPF의 UI 스레드에서 화면 업데이트하고 싶다면,
DispatcherTimer System.Threading.Timer는 callback이 겹칠 수 있다. 비동기 처리를 거칠게 쑤셔 넣으면 거칠어지기 쉽다DispatcherTimer는 UI를 직접 건드릴 수 있는 대신, 무거운 처리를 넣으면 UI째로 멈추기 쉽다- 지난번 소프트 리얼타임의 문맥에서는, 이 3가지는 고정밀 대기의 주역이 아니다
요컨대 처음에 봐야 할 것은 다음 3가지입니다.
- 어느 스레드 / 컨텍스트에서 동작시키고 싶은가
- 처리 본체를
async/await로 직렬로 쓰고 싶은가 - callback의 겹침을 허용할 수 있는가
이 3가지를 나누는 것만으로도 꽤 망설이기 어려워집니다.
2. 먼저 한 장으로 정리
2.1. 전체상
flowchart LR
A["일정 간격으로 뭔가 하고 싶다"] --> B{"UI 스레드에서 동작시키고 싶은가?"}
B -- "예" --> C["DispatcherTimer"]
B -- "아니오" --> D{"처리 본체를<br/>async / await로<br/>자연스럽게 쓰고 싶은가?"}
D -- "예" --> E["PeriodicTimer"]
D -- "아니오" --> F{"ThreadPool에서<br/>가벼운 callback을<br/>돌리고 싶은가?"}
F -- "예" --> G["System.Threading.Timer"]
F -- "아니오" --> H["다른 설계도 검토<br/>Channel / BackgroundService / event / waitable timer"]
실무에서는 대개 이 분기로 충분합니다.
망설였을 때 가장 빗나가기 어려운 것은,
비동기 처리라면 PeriodicTimer, UI 업데이트라면 DispatcherTimer로 먼저 자르는 것입니다.
System.Threading.Timer는 편리하지만, callback의 겹침이나 수명 관리의 버릇이 있으므로,
처음 1개째로서는 조금 까다롭습니다.
2.2. 먼저의 판단표
| 상황 | 먼저의 선택 | 실행되는 장소 | 어울리는 이유 | 먼저의 주의점 |
|---|---|---|---|---|
| 일정 간격으로 HTTP / DB / 파일 I/O 등의 async 처리를 돌리고 싶다 | PeriodicTimer |
지금의 async 메서드의 흐름 속 | await 베이스로 쓸 수 있고, 정지와 취소가 자연스럽다 |
1 타이머 1 컨슈머 전제. 지연은 마음대로 병렬화되지 않음 |
| 가벼운 heartbeat / 메트릭스 송신 / 캐시의 기한 만료 체크를 ThreadPool에서 돌리고 싶다 | System.Threading.Timer |
ThreadPool | 경량으로 callback형. 기존의 callback 베이스 설계에 태우기 쉽다 | callback은 재진입 가능 전제. 겹칠 수 있음. 참조를 유지한다 |
| WPF의 시계 표시나 가벼운 UI 업데이트를 일정 간격으로 돌리고 싶다 | DispatcherTimer |
WPF의 Dispatcher(UI 스레드) |
UI를 그대로 건드릴 수 있다. 우선도를 가질 수 있다 | 정확한 발화 시각은 보증되지 않음. 무거운 처리로 UI가 막힌다 |
주기의 정확성이 본체로, Sleep 맡기기를 피하고 싶다 |
이 3가지를 주역으로 하지 않는다 | - | 목적이 앱의 정기 실행이 아니라 대기 정밀도의 설계가 된다 | event / waitable timer 쪽을 본다 |
이 표에서 중요한 것은 타이머 이름보다 실행 장소와 쓰는 방법을 본다는 것입니다. 타이머 고르기로 사고날 때는, API 명칭보다 「어디서 달릴지」를 보고 있지 않은 경우가 많습니다.
3. 먼저 구별하고 싶은 것
3.1. callback형인가, tick을 기다리는 형인가
여기를 나누면 꽤 정리하기 쉬워집니다.
System.Threading.Timer와DispatcherTimer는 callback / event형PeriodicTimer는 tick을await해서 기다리는 형
즉,
- callback형은 「타이머 측이 불러온다」
PeriodicTimer는 「이쪽이 다음 tick을 기다린다」
는 차이입니다.
처리 본체가 async이며,
「기다린다 → 처리한다 → 또 기다린다」를 1개의 흐름으로 읽고 싶다면, PeriodicTimer쪽이 자연스럽습니다.
반대로,
- 기존의 callback 베이스 설계에 태우고 싶다
- 처리 본체가 짧고 동기적
- 단순히 정기 킥하고 싶다
는 장면에서는 System.Threading.Timer가 맞습니다.
PeriodicTimer는 편리하지만 만능이 아닙니다.
하나의 타이머에 대해 동시에 복수의 WaitForNextTickAsync를 날리는 전제가 아니며,
기다리고 있지 않은 동안에 복수 회 tick해도 그것은 1회로 접혀집니다.
여기를 「마음대로 따라잡아 준다」고 오해하지 않는 것이 중요합니다.
3.2. ThreadPool에서 동작하는가, UI 스레드에서 동작하는가
다음에 봐야 할 것은 어디서 실행되는가입니다.
System.Threading.Timer의 callback은 작성한 스레드가 아니라 ThreadPool에서 동작합니다.
그래서 백그라운드 처리에는 어울리지만, UI를 직접 건드릴 전제가 아닙니다.
한편 DispatcherTimer는 Dispatcher 큐에 통합된 UI용 타이머입니다.
WPF에서는 같은 Dispatcher 위에서 동작하므로, Tick 핸들러 안에서 UI를 그대로 업데이트할 수 있습니다.
이 차이는 꽤 큽니다.
- ThreadPool 타이머에서 UI를 건드리려면 명시적으로 UI로 돌려야 한다
DispatcherTimer는 UI를 건드리기 쉽지만, 그만큼 UI 스레드의 시간을 사용한다
즉, DispatcherTimer는 「UI를 안전하게 건드릴 수 있다」가 강점이지만,
그것은 동시에 「무거운 처리를 넣으면 입력이나 재묘화도 말려든다」는 의미이기도 합니다.
3.3. 주기 처리와 정밀도 보증은 별개의 이야기
여기는 지난번 기사와의 연결로서 중요합니다.
일정 간격으로 뭔가 한다, 라는 말하기 방식은 같아도,
- 앱의 사정으로서 몇 초마다 정기 처리하고 싶다
- 1ms~몇 ms급으로, 가능한 한 deadline에 가까이 가고 싶다
는 별개의 문제입니다.
System.Threading.Timer는 경량으로 다루기 쉬운 타이머지만,
정밀도를 위한 전용 도구가 아닙니다.
DispatcherTimer도 Dispatcher 큐의 사정이나 우선도의 영향을 받습니다.
PeriodicTimer도 이름만 보면 「주기가 딱딱해 보이는」 것처럼 보이지만,
실무에서의 강점은 precision이라기보다 async 플로우의 쓰기 쉬움입니다.
그래서,
- 앱의 정기 실행을 쓰고 싶은가
- 대기 정밀도를 다듬고 싶은가
는 처음에 나누는 편이 안전합니다.
이 2가지가 섞이면 타이머 고르기의 의론이 점점 이상한 방향으로 갑니다.
4. 전형 패턴
4.1. async한 정기 처리라면 PeriodicTimer
worker나 BackgroundService, 콘솔의 상주 처리 등에서
일정 간격으로 async한 처리를 돌리고 싶다면, 먼저 PeriodicTimer가 쓰기 쉽습니다.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public sealed class CacheRefreshWorker : BackgroundService
{
private readonly ILogger<CacheRefreshWorker> _logger;
public CacheRefreshWorker(ILogger<CacheRefreshWorker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("CacheRefreshWorker started.");
await RefreshCacheAsync(stoppingToken);
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));
try
{
while (await timer.WaitForNextTickAsync(stoppingToken))
{
await RefreshCacheAsync(stoppingToken);
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("CacheRefreshWorker stopping.");
}
}
private async Task RefreshCacheAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Refreshing cache...");
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
}
이 형태가 좋은 것은 다음 점입니다.
- 코드의 흐름이 1개의
async메서드로서 쫓기 쉽다 CancellationToken을 그대로 하류로 넘기기 쉽다- callback 베이스의 수명 관리나 예외 관리를 줄일 수 있다
특히 처리 본체가
- HTTP를 부른다
- DB에 질의한다
- 파일을 읽는다
- 다른 async API를 await한다
같은 I/O 대기 중심이라면 꽤 상성이 좋습니다.
주의점은 2가지입니다.
- 1 타이머 1 컨슈머 전제로 사용한다
- 처리 시간이 주기보다 길 때의 방침을 스스로 정한다
PeriodicTimer는 앞의 처리가 길어졌다고 해서 자동으로 병렬화해서 따라잡아 주는 것이 아닙니다.
그 의미에서는 「일정 간격의 async 루프를 자연스럽게 쓰기」 위한 타이머입니다.
테스트하기 쉬움까지 본다면, TimeProvider를 받는 컨스트럭터를 사용할 수 있는 것도 수수하게 편리합니다.
4.2. 가벼운 callback을 ThreadPool에서 돌린다면 System.Threading.Timer
정기적으로 짧은 callback을 부르고 싶은 것만이라면, System.Threading.Timer는 자연스럽습니다.
예를 들어,
- heartbeat를 친다
- 가벼운 메트릭스를 채취한다
- 짧은 기한 만료 체크를 넣는다
- 기존의 callback 베이스 설계에 매단다
같은 장면입니다.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public sealed class HeartbeatService : IHostedService, IDisposable
{
private readonly ILogger<HeartbeatService> _logger;
private Timer? _timer;
private int _running;
public HeartbeatService(ILogger<HeartbeatService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(OnTimer, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void OnTimer(object? state)
{
if (Interlocked.Exchange(ref _running, 1) != 0)
{
return;
}
try
{
_logger.LogInformation("Heartbeat: {Now}", DateTimeOffset.Now);
}
finally
{
Volatile.Write(ref _running, 0);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
이 예에서 Interlocked.Exchange를 넣고 있는 것은,
System.Threading.Timer가 이전 callback 완료를 기다리지 않기 때문입니다.
여기는 꽤 중요합니다.
- callback은 ThreadPool에서 동작한다
- callback은 재진입 가능 전제
- 간격보다 처리가 길면 겹칠 수 있다
그래서 처리가 가볍지 않은 경우는,
- 중복 기동을 스킵한다
- 큐에 쌓는다
PeriodicTimer로 기울인다
처럼 설계하는 편이 온화합니다.
또 하나 수수하게 중요한 것은 참조를 유지하는 것입니다.
System.Threading.Timer는 동작 중이라도 참조가 없어지면 GC의 대상이 됩니다.
또, Dispose()를 부른 직후라도, 이미 큐잉된 callback이 뒤에서 달리는 경우가 있습니다.
즉 System.Threading.Timer는,
- 경량
- 빠름
- 심플
이지만, 그 대신에 callback의 사정을 이쪽이 제대로 떠맡는 타이머입니다.
4.3. WPF의 UI 업데이트라면 DispatcherTimer
WPF에서 화면상의 시계나 가벼운 상태 표시를 정기 업데이트하고 싶다면, DispatcherTimer가 자연스럽습니다.
using System;
using System.Windows;
using System.Windows.Threading;
public partial class MainWindow : Window
{
private readonly DispatcherTimer _clockTimer;
public MainWindow()
{
InitializeComponent();
_clockTimer = new DispatcherTimer(DispatcherPriority.Background)
{
Interval = TimeSpan.FromSeconds(1)
};
_clockTimer.Tick += ClockTimer_Tick;
_clockTimer.Start();
}
private void ClockTimer_Tick(object? sender, EventArgs e)
{
ClockText.Text = DateTime.Now.ToString("HH:mm:ss");
}
protected override void OnClosed(EventArgs e)
{
_clockTimer.Stop();
_clockTimer.Tick -= ClockTimer_Tick;
base.OnClosed(e);
}
}
DispatcherTimer의 좋은 점은 Tick이 WPF의 Dispatcher 위에서 처리되는 것입니다.
그래서 UI를 그대로 건드릴 수 있습니다.
이것은 예를 들어,
- 시계 표시
- 접속 상태의 가벼운 표시 업데이트
- Command의 재평가 계기
- 화면에 나와 있는 수치의 가벼운 업데이트
같은 장면과 상성이 좋습니다.
다만, 여기서도 공기가 변하는 점이 있습니다.
DispatcherTimer는 UI 스레드에서 동작하므로,
Tick 핸들러에서 무거운 처리를 하면, 그대로 입력・묘화・재배치까지 말려들어 느려집니다.
또, DispatcherTimer는 「지정 시각 딱」을 보증하는 도구가 아닙니다.
Dispatcher 큐 상의 다른 일이나 우선도의 영향을 받습니다.
그래서 실무에서는,
- Tick의 내용은 가볍게 한다
- 무거운 I/O나 CPU는 별도로 도망시킨다
- 닫을 때는
Stop()과 구독 해제를 해 수명을 명시한다
정도까지 의식해 두면 안정됩니다.
4.4. 소프트 리얼타임 쪽의 주기 처리라면 다른 도구를 본다
여기가 지난번 기사와의 접속점입니다.
지난번 소프트 리얼타임 기사에서 다룬 것은, 「몇 초마다 대략 동작하면 좋은가」가 아니라, 주기의 흔들림이나 deadline miss를 어떻게 줄일까라는 이야기였습니다.
그 문맥에서는,
Sleep맡기기의 상대 대기로 하지 않는다- 이벤트 구동이나 waitable timer를 사용한다
- fast path와 slow path를 나눈다
- 지연을 계측한다
가 주제가 됩니다.
그래서,
- 일상 앱의 async한 정기 처리
→
PeriodicTimer - ThreadPool callback
→
System.Threading.Timer - UI 업데이트
→
DispatcherTimer - 주기 정밀도 자체가 주역 → 지난번 기사의 세계
라는 식으로, 처음부터 문제를 나누어 버리는 것이 깔끔합니다.
「1ms마다 가능한 한 딱 돌리고 싶다. 어느 .NET 타이머가 좋은가」라는 물음은, 절반 정도는 이미 타이머 고르기가 아니라 대기 방법과 설계의 문제입니다.
5. 자주 있는 안티패턴
5.1. System.Threading.Timer에 async 람다를 그대로 넘긴다
이것은 꽤 자주 하기 쉽습니다.
_timer = new Timer(async _ => await RefreshAsync(), null,
TimeSpan.Zero, TimeSpan.FromSeconds(5));
보기엔 깔끔하지만, TimerCallback은 void입니다.
즉 이 async 람다는 실질 async void적인 취급이 됩니다.
그러면,
- 호출 측이 await할 수 없다
- 완료를 기다릴 수 없다
- 예외 관리가 어렵다
- callback의 겹침도 별도로 생각할 필요가 있다
는 꽤 진흙탕의 상태가 됩니다.
처리 본체가 async라면 먼저 PeriodicTimer를 검토하는 편이 읽기 쉽습니다.
5.2. DispatcherTimer의 Tick에 무거운 처리를 넣는다
DispatcherTimer는 UI를 그대로 건드릴 수 있으므로, 무심코 뭐든지 쓰고 싶어집니다.
하지만 거기는 UI 스레드입니다.
- 긴 동기 처리
- 무거운 CPU 계산
- 블로킹 I/O
- 긴
await를 포함하는 이중 기동할 수 있는 처리
를 넣으면 UI의 입력이나 묘화와 정면 충돌합니다.
Tick의 내용은 가볍게 하고, 무거운 일은 배경으로 도망치고, 필요한 결과만 UI로 돌리는 편이 안정됩니다.
5.3. PeriodicTimer라면 지연을 자동으로 만회해 준다고 생각한다
여기도 오해하기 쉽습니다.
PeriodicTimer는 일정 간격의 async 루프를 깔끔하게 쓰는 도구로서는 우수하지만,
이전 처리가 길어졌을 때 마음대로 병렬 실행해서 따라잡아 주는 것이 아닙니다.
기다리고 있지 않은 동안의 tick이 1회로 접혀지는 경우도 있으므로,
- 지연되면 스킵할 것인가
- 최신만 보면 되는가
- 반드시 전 횟수 분 처리하고 싶은가
는 설계로 정할 필요가 있습니다.
5.4. 정지와 수명 관리를 뒤로 미룬다
타이머는 돌리기보다 멈추는 쪽이 사고납니다.
간과하기 쉬운 것은 예를 들어 다음입니다.
System.Threading.Timer를 로컬 변수 그대로 만들어 참조를 유지하지 않는다System.Threading.Timer를 멈추지 않고Dispose()주변을 애매하게 한다DispatcherTimer를Stop()하지 않고 Tick 구독도 빼지 않는다- 화면을 닫은 후에도 타이머가 오브젝트 수명을 끌어 당긴다
특히 DispatcherTimer는 메서드가 바인드되어 있는 오브젝트를 계속 살릴 수 있습니다.
「뭔가 이 Window, 닫았을 터인데 남아 있네」라는 묘한 느낌이 나오면, 여기를 의심하고 싶어집니다.
6. 리뷰 시의 체크리스트
- 그 주기 처리는 UI 업데이트 / ThreadPool callback / async 루프 중 어느 것으로 써야 할지 설명할 수 있는가
- 처리 본체가 async인데 callback형 타이머에 무리하게 밀어넣고 있지 않은가
System.Threading.Timer를 사용한다면, callback의 겹침에 견딜 수 있는가, 또는 가드하고 있는가DispatcherTimer의 Tick에 무거운 처리, 블로킹 I/O, 긴 동기 처리를 넣고 있지 않은가PeriodicTimer를 사용한다면, 지연되었을 때의 방침이 정해져 있는가- 정지 방법(
Change/Dispose/Stop)과 앱 종료 시의 흐름이 명확한가 System.Threading.Timer의 참조를 제대로 유지하고 있는가DispatcherTimer의 구독 해제나 화면 클로즈 시의 뒷정리가 있는가- 그 문제가 「앱의 정기 실행」인가 「대기 정밀도」인가, 처음에 나뉘어 있는가
7. 대강의 사용 구분
실무에서의 기준으로서는 대략 다음입니다.
-
30초마다 API를 쳐서 설정을 업데이트하고 싶다 →
PeriodicTimer -
5초마다 heartbeat나 가벼운 메트릭스를 보내고 싶다 →
System.Threading.Timer -
WPF에서 시계 표시나 가벼운 스테이터스 업데이트를 하고 싶다 →
DispatcherTimer -
Tick마다 UI를 직접 건드리고 싶다 →
DispatcherTimer -
정기 처리의 본체가
await투성이로, 정지나 예외도 자연스럽게 다루고 싶다 →PeriodicTimer -
callback 베이스의 작은 킥을 저비용으로 넣고 싶다 →
System.Threading.Timer -
1~5ms급의 주기 정밀도나 흔들림의 관리가 본체 → 이 3가지 전에, 지난번 기사의 대기 방법을 본다
꽤 난폭하게 1행으로 말하면,
PeriodicTimer는 async를 위한 타이머System.Threading.Timer는 ThreadPool callback을 위한 타이머DispatcherTimer는 UI를 위한 타이머
입니다.
이 외우는 법이라면 크게 빗나가기 어렵습니다.
8. 정리
.NET의 타이머 고르기에서 정말 중요한 것은 이름의 차이보다 다음입니다.
- 어디서 동작하는가
- 어떤 흐름으로 쓰고 싶은가
- 겹침이나 지연을 어떻게 다룰 것인가
먼저의 방침으로서는 다음으로 꽤 싸울 수 있습니다.
- async한 정기 처리라면
PeriodicTimer - 가벼운 callback을 ThreadPool에서 돌린다면
System.Threading.Timer - WPF의 UI 업데이트라면
DispatcherTimer - 정밀도가 주역이라면 다른 대기 방법을 본다
타이머는 이름이 닮아 있으므로 섞입니다. 하지만 역할은 그렇게 닮지 않았습니다.
PeriodicTimer는 async 플로우를 정돈하는 도구System.Threading.Timer는 callback을 정기 킥하는 도구DispatcherTimer는 UI 스레드에서 정기 업데이트하는 도구
이 3가지를 나누어 생각하는 것만으로도 코드는 꽤 조용해집니다.
반대로 여기가 섞이면,
- async인 줄 알았는데
async void처럼 된다 - UI를 직접 건드려서 떨어진다
- callback이 겹쳐서 상태가 흐려진다
- 주기 정밀도의 이야기까지 한꺼번에 되어 버린다
라는, 꽤 평범하게 귀찮은 일이 일어납니다.
먼저는 「어디서 동작시키고 싶은가」부터 본다. 그것만으로 타이머 고르기는 꽤 온화해집니다.
9. 참고 자료
- 관련 기사: 일반 Windows에서 소프트 리얼타임을 가능한 한 실현하기 위한 실천 가이드 - 먼저 보는 체크리스트
- 관련 기사: C#에서의 async/await의 베스트 프랙티스 - 먼저 보는 판단표
- 관련 기사: WPF / WinForms의 async/await와 UI 스레드를 한 장으로 정리 - await 후의 돌아갈 곳, Dispatcher, ConfigureAwait, .Result / .Wait()의 막힘 포인트
- Timers - .NET
- PeriodicTimer Class
- PeriodicTimer.WaitForNextTickAsync(CancellationToken) Method
- PeriodicTimer.Dispose Method
- PeriodicTimer Constructor
- Timer Class (System.Threading)
- Timer Constructor (System.Threading)
- Background tasks with hosted services in ASP.NET Core
- DispatcherTimer Class (System.Windows.Threading)
- DispatcherTimer Class (Microsoft.UI.Xaml)
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
Generic Host / BackgroundService를 데스크톱 앱에 가지고 들어오는 이유 - 기동・수명・graceful shutdown의 정리가 꽤 편해진다
WPF나 WinForms 같은 데스크톱 앱에서 Generic Host와 BackgroundService를 도입해 기동, 상주 처리, graceful shutdown, DI, 로그, 설정의 입구를 한 곳으로 모으는 설계 정리법과 안티패턴을 실무 시...
.NET의 Generic Host란 무엇인가 - DI, 설정, 로그, BackgroundService를 먼저 정리
Generic Host의 정체를, DI・설정・로그・BackgroundService와 Host.CreateApplicationBuilder / WebApplicationBuilder의 관계로 정리합니다. 어디서 효과적이고 어디서 과잉인지를 .NET...
.NET의 Native AOT란 무엇인가 - JIT, ReadyToRun, trimming과의 차이를 먼저 정리
Native AOT가 publish 시점에 .NET 앱을 정적으로 굳히는 배포 모델임을 JIT, ReadyToRun, trimming, source generator와 비교해 정리하고, 어느 앱에 잘 맞고 어디서 막히는지 실무 관점에서 판별 기준...
WPF / WinForms의 async/await와 UI 스레드를 한 장으로 정리 - await 후의 돌아갈 곳, Dispatcher, ConfigureAwait, .Result / .Wait()의 막힘 포인트
WPF/WinForms에서 async/await가 헷갈리는 핵심—await 후의 복귀 스레드, Dispatcher와 Invoke의 사용 구분, ConfigureAwait(false)의 진짜 의미, .Result/.Wait()로 화면이 굳는 이유까...
FileSystemWatcher 사용법과 주의점 - 누락, 중복 알림, 완료 판정의 함정
Windows .NET 파일 감시에서 FileSystemWatcher의 이벤트를 완료 알림으로 오인하기 쉬운 함정과, 재스캔 요청·원자적 claim·idempotency를 축으로 누락과 중복을 견디는 안전한 설계 패턴을 정리합니다.
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
UI 스레드 & 타이머
WPF / WinForms UI 스레드, async 흐름, Dispatcher 사용, 타이머 판단을 정리한 토픽 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
기술 상담 & 설계 리뷰
설계 방향, 아키텍처 경계, 수명 관리, 기존 Windows 자산 처리 방법을 정리하는 데 도움을 드립니다.
저자 프로필
기사 저자의 프로필 페이지입니다.
Go Komura
합동회사 코무라소프트 대표
Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.
공개 링크