DispatcherTimer со временем загружает процессор, вызывая неправильную визуализацию WPF - PullRequest
5 голосов
/ 10 октября 2009

У меня есть приложение WPF, которое использует DispatcherTimer для обновления отметки времени.

Однако после того, как мое приложение проработало около 6 часов, углы стрелок часов больше не меняются. Я проверил, что DispatcherTimer все еще запускается с Debug и что значения углов все еще обновляются, однако экранная визуализация не отражает изменения.

Я также проверил с помощью инструментов WPFPerf Visual Profiler, что Unlabeled Time, Tick (Time Manager) и AnimatedRenderMessageHandler (Media Content) все постепенно растут, пока они не потребляют почти 80% ЦП, однако память работает стабильно.

hHandRT.Angle является ссылкой на RotateTransform

hHandRT = new RotateTransform(_hAngle);

Этот код отлично работает примерно 5 часов подряд, но после этого он задерживается и изменение угла не отображается на экране. Любые предложения о том, как решить эту проблему, или возможные возможные решения.

.NET 3.5, Windows Vista SP1 или Windows XP SP3 (оба показывают одинаковое поведение)

РЕДАКТИРОВАТЬ: Добавление функции тактового сигнала

//In Constructor
...
_dt = new DispatcherTimer();
_dt.Interval = new TimeSpan(0, 0, 1);
_dt.Tick += new EventHandler(Clock_Tick);
...

 private void Clock_Tick(object sender, EventArgs e)
        {

            DateTime startTime = DateTime.UtcNow;
            TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById(_timeZoneId);
            _now = TimeZoneInfo.ConvertTime(startTime, TimeZoneInfo.Utc, tst);
            int hoursInMinutes = _now.Hour * 60 + _now.Minute;
            int minutesInSeconds = _now.Minute * 60 + _now.Second;
            _hAngle = (double)hoursInMinutes * 360 / 720;
            _mAngle = (double)minutesInSeconds * 360 / 3600;
            _sAngle = (double)_now.Second * 360 / 60;
            // Use _sAngle to showcase more movement during Testing.
            //hHandRT.Angle = _sAngle;
            hHandRT.Angle = _hAngle;
            mHandRT.Angle = _mAngle;
            sHandRT.Angle = _sAngle;

            //DSEffect
            // Add Shadows to Hands creating a UNIFORM light
            //hands.Effect = textDropShadow;
        }

По линии слишком большого количества событий, происходящих в такте часов, в настоящее время я пробую эту настройку, чтобы посмотреть, поможет ли это. Жаль, что ошибка проявляется через 5 часов: (

  //DateTime startTime = DateTime.UtcNow;
  //TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById(_timeZoneId);
  //_now = TimeZoneInfo.ConvertTime(startTime, TimeZoneInfo.Utc, tst);
  _now = _now.AddSeconds(1);

Ответы [ 3 ]

4 голосов
/ 13 октября 2009

Вы говорите, что каждый раз создаете экземпляр класса Clock? Обратите внимание, что таймеры в .NET будут рутировать себя, чтобы избежать сбора мусора. Они будут продолжать стрелять до тех пор, пока вы сами их не остановите, и они будут поддерживать ваши объекты Clock живыми, потому что на них есть ссылки в событии отметки времени.

Я думаю, что с каждым созданным вами часом вы запускаете новый таймер. Сначала вы запускаете только 1 событие в секунду, но затем вы получаете прибавку к другому таймеру и получаете 2 в секунду, и они продолжают накапливаться таким образом. В конце концов вы видите, что ваш обработчик Tick и AnimatedRenderMessageHandler растут в ЦП, пока они не отключатся и не смогут обновить ваш экран. Это также объясняет, почему увеличение частоты срабатываний таймера заставило ваши симптомы появиться раньше.

Исправление должно быть простым: просто остановите или утилизируйте DispatcherTimer, когда закончите с объектом Clock.

1 голос
/ 14 октября 2009
hHandRT.Angle = _hAngle;
mHandRT.Angle = _mAngle;
sHandRT.Angle = _sAngle;

Полагаю, вам нужно еще раз взглянуть на приведенный выше код.

Вы устанавливаете свойство Angle вашего преобразования для всех 3 преобразований, даже если вам не нужно, чтобы они менялись каждую секунду. Ваша минута будет меняться при каждых 60 изменениях, а ваш час - каждые 3600 секунд. Однако вы можете хотя бы уменьшить угол смены часов на каждую секунду.

Здесь происходит следующее: всякий раз, когда вы запрашиваете изменения преобразования в WPF, WPF помещает запрос в очередь приоритетной отправки, и каждую секунду вы отправляете больше изменений, чем оно может обработать. И это единственная причина, по которой нагрузка на процессор продолжает увеличиваться, а не на память.

Детальный анализ:

Посмотрев на ваш код, я чувствую, что ваше событие DispatcherTimer_Tick выполняет слишком много вычислений, помните, что поток Dispatcher уже перегружен множеством вещей, таких как управление маршрутизацией событий, визуальное обновление и т. Д., Если ваш процессор более загружен, чтобы выполнять пользовательские задача в потоке диспетчера, которая также при каждом втором событии будет определенно увеличивать очередь ожидающих задач.

Вы могли бы подумать, что это небольшой расчет умножения, но для потока Dispatcher это может быть дорогостоящим, когда дело доходит до загрузки часовых поясов, преобразования значения времени и т. Д. Вы должны профилировать и видеть время выполнения тика.

Вам следует использовать объект System.Threading.Timer, который будет запускаться в другом потоке, после каждого тикового события, когда вы закончите с вычислениями требуемых конечных углов, вы можете затем передать их потоку Dispatcher.

как

Dispatcher.BeginInvoke((Action)delegate(){
   hHandRT.Angle = _hAngle;
   mHandRT.Angle = _mAngle;
   sHandRT.Angle = _sAngle;   
});

Делая это, вы немного уменьшите рабочую нагрузку из потока диспетчера.

1 голос
/ 12 октября 2009

Вы предполагаете, что это DispatcherTimer и полностью сосредоточены на этом. Лично мне трудно поверить, что это как-то связано с самим таймером, но я думаю, что это связано с тем, что вы делаете в пределах отметки времени. Можете ли вы рассказать нам больше о том, что происходит каждый раз, когда срабатывает таймер?

...