간단한 시간 출력 기능, UI 갱신이 멈추는 경우

안녕하세요 연속으로 질문을 올리게 되었네요 미리 감사드립니다

문제로 넘어가서 wpf로 시간을 출력하는 프로그램을 만들었는데 꽤 자주 시간이 멈추는 모습을 보입니다

혹시 타이머가 멈추는 건가 싶어서 시간이 갱신 될 때마다 list에 넣고 문제 발생 시 중단해서 확인해보면 시간은 제대로 추가가 되어있는데요

주기가 100ms인데 이 정도면 너무 자주 갱신해서 성능에 영향을 주는 정도는 아닐 것 같은데 어떻게 접근하면 될까요

System.Threading.Timer를 만들고 100ms마다 DateTime.Now로 시간을 대입하는 간단한 프로그램입니다

  public abstract class ViewModelBase : ObservableObject
{
...
}

    public class MainViewModel : ViewModelBase
    {

  private DateTime _CurrentTime;
        public DateTime CurrentTime
        {
            get => _CurrentTime;
            set
            {
                SetProperty(ref _CurrentTime, value);
            }
        }

  public MainViewModel()
        {
            List<DateTime> date = new List<DateTime>();
            _uiTimer = new Timer(new TimerCallback((s) =>
            {
                date.Add(DateTime.Now);
                if (date.Count > 100)
                    date.Clear();
                CurrentTime = DateTime.Now;
            }),
            null, TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
        }

...

// *.xaml
        <Label Content="{Binding CurrentTime}" ContentStringFormat="yyyy-MM-dd hh:mm:ss.fff"/>
좋아요 1

올려주신 예제대로 테스트를 해 봤을때 별다른 문제는 없었는데요.
차이점이 있다면 별도의 TaskPeriodicTimer 타이머를 사용하였습니다.
테스트 했던 코드 공유 드릴게요.

public class MainViewModel : ObservableObject, IDisposable
{
    private DateTime _currentTime;
    private PeriodicTimer _timer;
    private Task _timerTask;

    /// <inheritdoc />
    public MainViewModel()
    {
        StartTimer();
    }

    public DateTime CurrentTime { get => _currentTime; set => SetProperty(ref _currentTime, value); }

    /// <inheritdoc />
    public void Dispose()
    {
        _timer.Dispose();
        _timerTask.Dispose();
    }

    /// <summary>
    /// 타이머
    /// </summary>
    private void StartTimer()
    {
        _timer = new PeriodicTimer(TimeSpan.FromMilliseconds(100));

        _timerTask = Task.Run(async () =>
        {
            while (await _timer.WaitForNextTickAsync().ConfigureAwait(false))
            {
                CurrentTime = DateTime.Now;
            }
        });
    }
}
<Label Content="{Binding CurrentTime}"  ContentStringFormat="yyyy-MM-dd hh:mm:ss.fff" />
좋아요 3

.net 4.8이라 해당 타이머는 쓸 수가 없네요 ㅠ
답변 감사합니다

좋아요 1

올려주신 코드 그대로 테스트를 해 보니 중간에 한번씩 짧은 시간 멈추는 모습을 보였는데요.
아마도 Clear 함수 때문 인것으로 보입니다.
만약에 의도하신 Clear 함수가 100개를 유지하기 위함이라면 자료구조를 LinkedList로 바꾸고 100개가 넘을 때 마지막 원소만 삭제하는 방식으로 구성하시면 어떨까 싶네요.

/// <summary>
/// 타이머
/// </summary>
private void StartTimer()
{
    LinkedList<DateTime> date = new LinkedList<DateTime>();
    _timer = new Timer(new TimerCallback((s) =>
        {
            date.AddFirst(DateTime.Now);
            if (date.Count > 100)
            {
                date.RemoveLast();
            }
            CurrentTime = DateTime.Now;
        }),
        null, TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
}
좋아요 2

그건 테스트를 위해서 추가한건데 없애도 동일 증상이 발생합니다

다만 특이사항이 있는데 창에 뭔가 액션을 취하면 시간이 다시 흐르네요

예를들어 버튼을 하나 추가하고 시간이 멈췄을 때 버튼에 마우스를 올리면 시간이 흐르고
창을 잡고 조금씩 움직이면 멈추는 현상이 발생 안 합니다

좋아요 1

ui thread 우선순위 등 관련 있던 것으로
dispatcher timer 를 사용하고 priority를 send, databind등 우선순위 높은 것으로 설정하니 멈춤 없이 동작합니다
단 각 옵션 별 영향이 어떤지는 더 알아봐야할 것 같네요

좋아요 3

제가 오래전에 WPF를 만져서 기억이 가물가물한데…
Winform에서 (object).Invoke 하듯이, WPF에서도 Dispatcher.Invoke를 써야했던 기억이 있습니다.
Timer 이벤트 안에서 그냥 ui 변경과 관련된 무언가를 시도하면 Cross-thread 오류가 날겁니다.
(ObservableObject 상속받았고, Label 바인딩을 하고 있기 때문에 아마 맞을듯…)
물론 그럴 때는 예외도 잘 발생되지 않아서 찾기가 힘들죠… :sweat_smile:

좋아요 1

Ctrl + F5 나 바이너리로 실행해보셨나요?

VS 디버그가 아닌 바이너리 실행에서는 전혀 문제가 없네요.
아마도 디버거 개입으로 인해 느려지는 현상이 있는 거 같슴다.

재밌는 건 100ms 보다 작은 값으로 디버그를 돌리면 오히려 덜 멈춘다는 거…=ㅅ=;;;

좋아요 3

그럴수도 있군요 참고하겠습니다!

좋아요 2

확실히 디버그 없이 실행해보니까 괜찮네요

그나저나 MVVM의 시작을 올려주신 글을 보고 했는데 여기서 뵈니까 신기하네요
코끼리가 눈에 익더라구요 ㅎㅎ
도움이 많이 되어 감사드립니다

좋아요 2