이 포스트에서는 C#에서 Garbage Collector의 영향과 이에 따른 Timer클래스의 안전한 사용 방법에 대해 정리해보고자 한다.
Garbage Collector(GC)의 역할
Garbage Collector는 .NET Framework에서 메모리 관리를 자동화하는 중요한 요소이다. GC는 더 이상 사용되지 않는 객체들을 감지하고 이들을 메모리에서 해제하여 리소스 누수를 방지한다. 하지만 때때로 GC의 작동 방식으로 인해 예상치 못한 결과가 발생할 수 있다. 특히 Timer 클래스와 같이 주기적으로 호출되어야 하는 기능을 구현할 때에 주의가 필요하다.
Timer 클래스의 일반적인 사용 방법과 GC의 영향
보통 Timer클래스를 사용하여 반복적인 작업을 수행하도록 프로그램을 구현할 수 있다.
예를 들어, 매일 자정에 호출되는 기능을 구현하기 위해 다음과 같이 코드를 작성하였다.
예제 코드1(fail):
private void OnTimerChanged(object state)
{
// 기능 실행
DoSomething();
// 다음 호출까지 남은 시간 계산
DateTime now = DateTime.Now;
DateTime midnight = now.Date.AddDays(1);
TimeSpan timeUntilMidnight = midnight - now;
// Timer 인스턴스 생성 후, 다음 호출까지 대기 후, 하루 간격으로 지속 호출
Timer timer = new Timer(_ => {
DoSomething();
}, null, timeUntilMidnight, TimeSpan.FromDays(1));
}
한번 DoSomething이 실행되고 나서, 다음날까지의 자정까지의 시간을 구해서 다음날 자정에 또 DoSomething이 실행되고, 그 후로부터는 TimeSpan.FromDays(1) 값으로 1일의 간격을 가지고 매 자정마다 수행하게 하였다.
그러나, 처음 실행은 잘 동작하지만, Garbage Collector에 의해 timer 인스턴스가 수집되는 상황이 발생한다. 따라서 프로그램이 실행된 후 오랜 시간이 지난 후에는 timer 콜백이 호출되지 않는 문제가 발생했다.
안전한 Timer 클래스 사용 방법
이 문제를 해결하기 위해 Timer 인스턴스를 클래스의 멤버 변수로 선언하여 가비지 컬렉터의 수집 대상에서 제외한다. 또한 Timer의 콜백 주기를 적절하게 조절하여 매일 자정에 호출되도록 보장한다.
예제 코드2(success):
public class SafeTimerExample
{
private Timer timer;
public SafeTimerExample()
{
// Timer 인스턴스 생성
timer = new Timer(OnTimerChanged, null, Timeout.Infinite, Timeout.Infinite);
// 최초 실행
timer.Change(0, Timeout.Infinite);
}
// Timer state callback 메서드
private void OnTimerChanged(object state)
{
// 기능 실행
DoSomething();
// 다음 호출까지 남은 시간 계산
DateTime now = DateTime.Now;
DateTime midnight = now.Date.AddDays(1);
TimeSpan tsUntilMidnight = midnight - now;
long msUntilMidnight = Math.Max(0L, (long)tsUntilMidnight.TotalMilliseconds);
// Timer의 다음 change 기간 설정
timer.Change(msUntilMidnight, Timeout.Infinite);
}
}
위 코드에서는 Timer 인스턴스를 멤버 변수로 유지함으로써 GC에 의해 수집되는 것을 방지한다. 또한 Timer의 콜백 주기를 적절하게 조절하여 매일 자정에 호출되도록 보장한다.
결론
Timer 클래스를 사용하여 반복적인 작업을 수행할 때는 Garbage Collector의 영향을 고려해야 한다. Timer 인스턴스를 적절하게 유지하고 콜백 주기를 조절하여 안정적으로 프로그램이 동작하도록 보장해야 한다. 이를 통해 메모리 누수와 예상치 못한 동작으로부터 안전하게 프로그램을 구현할 수 있다.
'Language > C#' 카테고리의 다른 글
C#에서 비동기 프로그래밍 이해하기 - Task와 async/await의 활용 (0) | 2024.10.26 |
---|---|
[C#] iteration 안에 await문을 넣으면 안되는 이유 (0) | 2024.01.18 |
[.NET] System.Threading.Timer Class에 대해 (0) | 2023.07.20 |
[.NET] Thread Pool에 대해 이해하기 (0) | 2023.07.20 |
[.NET] Garbage Collector에 대해 (0) | 2023.07.20 |