System.Threading.Timer를 이용해 타이머 작업을 할 때 유의할 점
이전에도 설명했지만, System.Threading.Timer는 그 기반을 Win32 API인 CreateTimerQueueTimer 함수에 두고 있습니다.
C++ CreateTimerQueue, CreateTimerQueueTimer 예제 코드
; https://www.sysnet.pe.kr/2/0/11090
이 때문에 GC가 있긴 해도 System.Threading.Timer는 IDisposable을 구현하고 있으므로 가능한 Dispose를 명시적으로 불러주는 게 자원 관리상 좋습니다. 결국, 다음과 같이 코딩할 수 있는데요.
using System;
class Program
{
static void Main(string[] args)
{
new WorkItem(5, 0);
Console.ReadLine();
}
class WorkItem
{
public int Arg;
System.Threading.Timer _timer;
public WorkItem(int arg, int interval)
{
Arg = arg;
_timer = new System.Threading.Timer(timerCallback, this, interval, 0);
}
private static void timerCallback(object state)
{
WorkItem workItem = state as WorkItem;
if (workItem == null)
{
return;
}
if (workItem._timer == null)
{
#if DEBUG
throw new ApplicationException("workItem._timer == null");
#else
return;
#endif
}
workItem._timer.Dispose();
}
}
}
위의 코드는 C++에서 설명했던 것과 유사한 문제에 빠집니다. 즉, _timer 변수에 값이 들어가는 것과, "new System.Threading.Timer"로 인해 타이머 대기 작업이 큐에 들어가 실행되는 시점이 차이가 발생한다는 점입니다. 경우에 따라, timerCallback 메서드가 실행되는 시점에 아직 _timer 변수가 초기화되지 않는 경우가 있습니다. (실제로, 제가 만든 응용 프로그램에서 아주 가끔씩 발생합니다.)
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
첨언하자면, 이 글의 예제에서 사용한 타이머 대기 작업 사용 방법은,
new System.Threading.Timer(timerCallback, this, 0, 0);
결국 다음의 작업과 동일한 것입니다.
ThreadPool.QueueUserWorkItem(workFunc, 6);
static void workFunc(object state)
{
Console.WriteLine(state);
}
따라서 System.Threading.Timer의 마지막 2개의 인자에 0, 0을 지정하는 것이라면 ThreadPool.QueueUserWorkItem을 사용하는 것이 더 안전합니다.
하지만, 그 외의 경우라면 어떻게 해서든 안전하게 실행시켜야 할 텐데요. 그때는 어쩔 수 없을 것 같습니다. 다음과 같은 식으로 약간의 조악한 안전장치를 넣어두어야겠지요.
private static void timerCallback(object state)
{
WorkItem workItem = state as WorkItem;
try
{
if (workItem == null)
{
return;
}
int tryCount = 5;
while (workItem._timer == null && tryCount-- > 0)
{
Thread.Sleep(10);
}
if (workItem._timer == null)
{
#if DEBUG
throw new ApplicationException("workItem._timer == null");
#else
return;
#endif
}
Console.WriteLine(workItem.Arg);
workItem._timer.Dispose();
}
catch
{
return;
}
}
혹시 더 좋은 의견 있으신 분은 덧글 부탁드립니다. ^^
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]