백구의 코딩찌개
  • [C#] WPF DispatcherTimer 으로 UI 업데이트 시, 버벅임 현상
    2024년 01월 24일 14시 25분 48초에 업로드 된 글입니다.
    작성자: 코딩백구
    반응형

    문제 현상

    실시간 차트를 구현중에 있었는데, 이때 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;
      }
    반응형
    댓글