Предотвращение переполнения событий обновления графического интерфейса в .NET

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

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


person Community    schedule 10.03.2010    source источник


Ответы (5)



Несколько вариантов, которые пришли мне в голову:

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

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

person CMerat    schedule 10.03.2010

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

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

person Hans Passant    schedule 10.03.2010
comment
+1 Я реализовал это решение раньше ... похоже, что такой хак имеет приватную переменную lastMessageSentAt только для того, чтобы обойти насос сообщений, но что бы ни работало ... - person Tanzelax; 11.03.2010

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

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

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

person Henk Holterman    schedule 10.03.2010
comment
@Tanzelax, я так понимаю, ты начал до моего редактирования. - person Henk Holterman; 11.03.2010
comment
Ха, да. Тем не менее, я думал, что он довольно ясно заявил, что его проблема заключается в том, что он слишком много вызывает, а не слишком много аннулирует. - person Tanzelax; 11.03.2010

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

person pdr    schedule 10.03.2010