WPF в реальном времени многопоточное приложение для торговли акциями

Я создаю многопоточное приложение в режиме реального времени в WPF, но у меня возникают трудности с обновлением пользовательского интерфейса.

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

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

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

Спасибо заранее!

Ответ 1

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

  • Существует некоторая дорогостоящая операция маршалинга, которая обеспечивает безопасное выполнение обновления для пользовательского интерфейса (по крайней мере, должно быть).
  • Рабочий поток получает диктует, как часто должен обновляться пользовательский интерфейс и, следовательно, сколько работы он должен выполнять. Он может легко перегружать насос сообщений.

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

[Worker-Thread] -> [Queue] -> [UI-Thread]

Сначала я начинал с простого подхода к очереди, но вы могли бы перейти к следующему логическому шагу создания конвейера, в котором есть 3 потока, участвующих в потоке обновлений. Рабочий поток помещает обновления, а поток пользовательского интерфейса деактивирует их, как раньше. Но новый поток может быть добавлен в микс, который управляет количеством обновлений, ожидающих очереди, и сохраняет его в управляемом размере. Он будет делать это, пересылая все обновления, если очередь остается маленькой, но переключится в безопасный режим и начнет отбрасывать обновления, которые вы можете прожить, или объединить их в одно, если можно определить разумную операцию слияния. Вот простая схема того, как этот шаблон может работать.

[Worker-Thread] -> [Queue-1] -> [Pipeline-Thread] -> [Queue-2] -> [UI-Thread]

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

Ответ 2

Вероятно, вам необходимо объединить полученные события, чтобы не каждый тик в результате обновлял графический интерфейс. Запустите их, если ваш графический интерфейс уже обновлен, а процесс GUI - следующий пакет, только когда он готов. Если канал имеет большой объем (часто в случае активных обновлений торговых данных), вы не сможете создать графический интерфейс, который отражает каждый отдельный тик в качестве собственного автономного триггера обновления.