Предотвращение потоков обновления графического интерфейса в .NET - PullRequest
2 голосов
/ 11 марта 2010

Я создаю систему, которая принимает показания с аппаратного устройства, которое отправляет данные через последовательный порт. Каждый раз, когда «пакет» поступает с последовательного порта, я обновляю и перерисовываю некоторые компоненты графического интерфейса для отображения обновленной информации. Так как события последовательного порта происходят из отдельного потока, я должен вызвать Invoke (Invalidate) для нескольких компонентов, чтобы заставить их перерисовать, и это может потенциально привести к тому, что графический интерфейс пользователя отстанет от данных последовательного порта, так как графический интерфейс поставит в очередь кучу Invoke () запрашивает, начинает ли аппаратное устройство отправлять, скажем, 500 пакетов в секунду.

Есть ли способ выяснить, существует ли уже запрос Invoke (Invalidate) для компонента GUI, я могу помешать коду поставить в очередь кучу из них, или я должен полностью использовать другой подход для обновления моих компонентов GUI

Ответы [ 5 ]

2 голосов
/ 11 марта 2010

Лучший вариант для этого, который я видел, это использовать новую Rx Framework .

Используя инфраструктуру Rx, вы можете превратить ваши события последовательного порта в IObservable<T>. Если вы сделаете это, IObservable<T> предоставит метод расширения Throttle, который позволит вам «отбросить назад» потоки шумных событий, делая их более управляемыми.

1 голос
/ 11 марта 2010

Сначала проверьте и измерьте. Invalidate() - очень (очень) дешевая функция. Пока вы не заставляете рисовать (Update, Refresh), у вас вряд ли будут проблемы с производительностью.

Ваше первое беспокойство должно (вероятно, теперь я догадываюсь) быть количеством Invokes, облагающих налогом MessagePump. Может быть, ваша полученная логика может немного сэкономить.

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

1 голос
/ 11 марта 2010

Имейте в виду, что вы обновляете то, что предназначено для человеческих глаз. Мы не видим ничего, кроме размытия, когда эти обновления происходят не быстрее, чем 25 раз в секунду. Таким образом, буферизуйте данные, которые вы получаете от SerialPort, и не начинайте / Invoke (), пока не пройдут последние 50 мсек с момента последнего вызова.

Либо DateTime.UtcNow, либо Environment.TickCount позволяет вам измерять это с достаточной точностью. У вас не должно возникнуть проблем, чтобы избежать остановки потока пользовательского интерфейса с такой скоростью.

1 голос
/ 11 марта 2010

Несколько вариантов на моей голове:

1) Использование переменной экземпляра в качестве флага: для _updatePending задано значение True перед вызовом, значение false при вызове invoke. Не вызывайте Invoke (Invalidate), если флаг равен True.

2) Вместо этого используйте механизм опроса: обновляйте графический интерфейс каждые X мс из источника данных, который обновляется в фоновых потоках.

0 голосов
/ 11 марта 2010

Я был бы склонен поместить всю необходимую вам информацию в контейнер Threadasafe Queue и иметь какой-то насос сообщений (проще говоря, Таймер) в вашем GUI, который считывает все элементы из этой очереди.

...