2016-10-27 9 views
0

У меня есть HID-устройство, с которым я общаюсь с частотой 200хГц-600 Гц, и интерпретирует данные в объект класса, который представляет свойства устройства HID. Класс реализует INotifyPropertyChanged по своим свойствам, и из-за скорости связи, я думаю, что очередь обработки увязнет, ​​поскольку элементы управления, похоже, становятся лагги и «обрамлены» через пару минут.INotifyPropertyChanged boggs down при обработке большого количества данных

Существуют ли методы в .net, которые могут помочь в таких вопросах, возможно, в пуле обработчиков событий или в какой-то очереди?

К сожалению, без моего HID устройства, я не уверен, что мой код никакой пользы никому повторить, но плохо включать пару нужных фрагментов, чтобы показать свою реализацию:

public enum DataEvents { onNone = 0, onStatus = 1, onInput = 2, onOutput = 4, onReport = 8}; 
public class Controller: INotifyPropertyChanged, IDisposable, INotifyDisposed 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 
    public event EventHandler Disposing; 
    public event EventHandler Disposed; 
    public event EventHandler ReportReceived; 

    internal void callPropertyChanged(string PropertyName) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName)); 
    } 

    internal void callReportReceived() 
    { 
     ReportReceived?.Invoke(this, EventArgs.Empty); 
    } 

    public bool Touch1 
    { 
     get { return _Touch1; } 
     private set { if (value != _Touch1) { _Touch1 = value; if (RaiseUpdateEvents.HasFlag(DataEvents.onInput)) callPropertyChanged("Touch1"); } } 
    } 
    private bool _Touch1 = false; 

    //There are many more properties but all of them follow this pattern, and have several different types 
} 

Мой объект заполняется из a System.Threading.Thread в цикле, который опросит устройство HID для отчетов. Метод чтения устройств HID является блокирующим вызовом, поэтому цикл не является циклом смерти и ограничен скоростью передачи данных устройства, как обычно указано 200хГц-600 Гц.

EDIT: Стоит отметить, что да, меня особенно интересуют привязки WPF.

+0

Если это Wpf, вы вызываете отставание диспетчера. Повысить событие реже – Gusdor

+0

@Gusdor Ну, событие возникает, когда свойство устройства HID изменяется, поэтому как бы уменьшить его – Wobbles

+0

@Gusdor Im думает о очереди для каждого свойства, где, если несколько событий подняты для свойства, оно очистит все, кроме самое последнее, но я не могу видеть, как это не добавит запаздывания и задержки в себе, особенно на этих скоростях. – Wobbles

ответ

1

Позволяет сделать что-то простое. Так или иначе, вам понадобится downsample ваши данные. Отображение данных в пользовательском интерфейсе на 200хГц-600Гц обычно вызывает проблемы.

Мое предложение - запустите таймер по своему выбору с максимальной продолжительностью, с которой вы в порядке. Давайте начнем с 1000 мс.

  1. Каждый раз, когда истекает таймер, срабатывает событие отключения для всех ваших собственности изменили события
  2. Не поднимайте свойство изменилось события, когда обновления устройств

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

+0

Для аппаратного взаимодействия даже при 100 мс таймер неприемлем. Я думаю, что мой ключ - это какая-то очередь, где, если события для правильной работы начинают складываться, это берет только самое последнее значение. – Wobbles

+1

@ Wobbles 100ms должно быть абсолютно нормально. 20 мс тоже должны хорошо работать. Важно то, что вы не бежите как можно быстрее, так как это всегда будет причиной узкого места.Я привел 1 секунду в качестве примера, потому что он будет очень заметен в пользовательском интерфейсе и в любом протоколе отладки, который вы можете удалить. – Gusdor

+2

«Аппаратное взаимодействие» на этой шкале не следует связывать с графическим интерфейсом. –

6

Когда вы работаете с системами реального времени в WPF (я работал последние 6 лет над ними), у вас есть пара вариантов. Во-первых, я перечислю пару кусочки пищи для размышлений:

  • Чтобы получить все привязки WPF для обновления через одно событие использовать string.Empty в качестве имени свойства.
  • Возможно, ваша проблема связана не только с событиями. WPF имеет довольно много ошибок, которые влияют на управление памятью.

Итак, вопрос, который вы задаете, - это то, как часто пользователю нужно видеть какие-либо изменения? Постоянство зрения человека составляет 1/10 секунды, или 100 мс. Любое обновление, более частое, чем это тратится впустую, но чаще всего, даже это слишком часто.

Один случай в секунду?

В моем сценарии мы определили, что нам нужно всего лишь обновлять все на экране один раз в секунду. Несмотря на то, что мы получили данные до 12 раз в секунду (83 мс на образец), мы собрали и усреднили данные для его сглаживания. Это обеспечило лучшее представление о том, что происходит для наших пользователей.

  • Мы сконструировали модели нашего вида, чтобы вызвать метод Update() один раз в секунду с мастер-таймером.
  • Модели реализованы INotifyPropertyChanged избежать binding memory leak, но только поднял событие изменения один свойство с string.Empty вызвать пользовательский интерфейс для обновления

Минимизация создания объекта

Каждый миллисекунды провел в сбор мусора заметное количество времени, которое пользователь не может взаимодействовать с вашим приложением. Каждый раз, когда вы поднимаете событие, вы должны создать объект события, который вы отправляете. Хотя вы технически можете создать объект события один раз и создать один и тот же экземпляр, есть несколько мест, где WPF создает для вас экземпляры объектов. Это то, что вам нужно, чтобы следить за:

  • DataTemplate по существу создает новый шаблон для каждого объекта, который вы хотите создать. По возможности попробуйте использовать виртуализацию и минимизируйте ее использование.
  • ResourceDictionary Каждый раз, когда вы объявляете словарь ресурсов в элементе управления, вы создаете новый экземпляр. Лучше иметь все ваши объединенные ресурсные словари в вашем App.xaml, чем содержать в том числе те же словари в разных пользовательских элементах управления. В частности, если у вас есть пользовательское управление в DataTemplate
  • ContentPresenter - не ваш друг.

Чтобы объяснить немного дальше, ContentPresenter будет ваш объект, посмотреть его тип в контрольной ResourceDictionary найти DataTemplate инстанцировать для ваших данных. Это может быть удобно, когда вам нужно поменять местами определенную часть вашего окна на другой элемент управления, однако это происходит за значительную стоимость. Минимизируйте его использование, когда это возможно.

Keep Оборудование/Связь в фоновом режиме

Мы создали темы, в частности, для обработки сообщений и потребностей обработки. Это позволяет пользовательскому интерфейсу реагировать, пока мы можем сделать немного DSP/статистическое сокращение данных.

Используйте Memory Profiler

Каждый раз, когда вам нужно рядом обновлений в режиме реального времени на дисплее, вы должны быть особенно осторожны с использованием памяти. Это была проблема номер один, с которой нам пришлось бороться.

  • Ваше приложение начинается нормально, но через минуту или две он начинает разлагать
  • Убедитесь, что вы не держите на экземпляры объектов, которые вы не ожидаете
  • Посмотрите на объекты, которые выживают мусор сбор события
-1

много полезной информации обсуждались здесь, но, насколько реализация, это путь, я пошел:

private Thread PropertyChangedQueueThread; 
    private List<string> ChangedPropertiesQueue = new List<string>(); 
    private ManualResetEvent PropertyChangedQueueBlocker = new ManualResetEvent(false); 

    private void PropertyChangedQueueWorker() 
    { 
     while (!this.disposingValue) 
     { 
      PropertyChangedQueueBlocker.WaitOne(); 

      string PropName = ChangedPropertiesQueue.Last(); 
      ChangedPropertiesQueue.RemoveAll(i => i == PropName); 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropName)); 

      if (ChangedPropertiesQueue.Count() == 0) 
       PropertyChangedQueueBlocker.Reset(); 
     } 
    } 

    internal void QueuePropertyChangedEvent(string PropertyName) 
    { 
     ChangedPropertiesQueue.Add(PropertyName); 
     PropertyChangedQueueBlocker.Set(); 
    } 

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

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

+0

Вы можете уменьшить сложность, просто отправив 'string.Empty' в качестве вашего имени свойства. Это позволяет избежать потенциальной ловушки частей вашего пользовательского интерфейса, которые не обновляются, поскольку имя свойства оказалось похороненным и отброшено. –

+0

@BerinLoritsch вы имеете в виду в последнем событии invoke? – Wobbles

+0

@BerinLoritsch Производительность, забитая при этом, подозревает, что слишком много свойств, и не все из них постоянно изменяются для полного обновления, чтобы иметь какое-либо значение производительности. – Wobbles