문제 현상
실시간 차트를 구현중에 있었는데, 이때 Timer 로 DispatcherTimer 를 사용하였다.
다 만들고 작동시키니 일반적인 상황에서는 문제가 없었는데, Timer 의 Tick 에 넣어준 이벤트의 처리속도가 지연 (System.Threading.Thread.Sleep 로 강제 지연 발생시켜줌) 되면 전체 UI도 함께 버벅이는 현상을 확인할 수 있었다.
원인
DispatcherTimer 는 WPF UI Thread 를 사용하기 때문에, UI 컨트롤에 자유롭게 접근할 수 있고 비교적 안전하다는 장점이 있지만,
단점 또한 UI Thread 를 사용하기 때문에, 이벤트 핸들러 처리 시간이 긴 작업의 경우 UI 가 Hang 된 느낌 (렉 걸린듯 버벅이는 현상) 을 줄 수가 있다는 단점이 있다.
해결
System.Timers.Timer 는 Multi Threading 을 지원하므로, UI Thread 와는 다른 Thread 에서 작업이 실행된다.
따라서 UI 컨트롤을 변경해주기 위해서 Invoke 또는 BeginInvoke 메서드로 실행시켜주었더니 앞에 발생했던 문제들이 해결되었다.
코드
- 기존의 코드 (DispatcherTimer 사용)
private DispatcherTimer InitDispatcherTimer(int period)
{
var timer = new DispatcherTimer(DispatcherPriority.Render);
timer.Interval = TimeSpan.FromMilliseconds(period);
timer.Tick += (s, e) =>
{
this.Dispatcher.BeginInvoke((Action)delegate ()
{
// Do something
using (lineData.SuspendUpdates())
{
if (DataQueue?.Count > 0)
{
Dictionary<string, string> data;
DataQueue.TryDequeue(out data);
var (dateTime, value) = parseData(data);
AppendData(dateTime, value);
sciChartSurface.XAxis.VisibleRange = new DateRange(DateTime.Now.AddSeconds(xRangeSeconds), dateTime);
}
}
});
System.Threading.Thread.Sleep(3000);
};
return timer;
}
- 수정한 코드 (Systems.Timers.Timer 사용)
private Timer InitTimer(int period)
{
var timer = new Timer();
timer.Interval = period;
timer.Elapsed += (s, e) =>
{
// BeginInvoke 사용 & Safety Option 사용
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (Action)delegate ()
{
using (lineData.SuspendUpdates())
{
if (DataQueue?.Count > 0)
{
Dictionary<string, string> data;
DataQueue.TryDequeue(out data);
var (dateTime, value) = parseData(data);
AppendData(dateTime, value);
sciChartSurface.XAxis.VisibleRange = new DateRange(DateTime.Now.AddSeconds(xRangeSeconds), dateTime);
}
}
});
};
return timer;
}