Поддержание адаптивности графического интерфейса пользователя во время выполнения длительных задач

StackOverflow https://stackoverflow.com/questions/148963

  •  02-07-2019
  •  | 
  •  

Вопрос

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

Вот хорошая дискуссия о том, как это сделать в wxPython.Подводя итог, можно сказать, что есть 3 способа:

  1. Используйте потоки
  2. Используйте wxYield
  3. Разделите работу на части и выполните ее в обработчике событий IDLE

Какой метод есть ты признано ли это наиболее эффективным ?Методы из других фреймворков (таких как Qt, GTK или Windows API) также приветствуются.

Это было полезно?

Решение

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

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

Другие советы

Определенно нити.Почему?Будущее за многоядерностью.Почти любой новый процессор имеет более одного ядра, или если у него только одно, он может поддерживать гиперпоточность и, таким образом, притворяться, что у него больше одного.Чтобы эффективно использовать многоядерные процессоры (а Intel планирует в недалеком будущем увеличить число ядер до 32), вам необходимо несколько потоков.Если вы запускаете все в одном главном потоке (обычно основным потоком является поток пользовательского интерфейса), у пользователей будут процессоры с 8, 16 и однажды 32 ядрами, и ваше приложение никогда не использует более одного из них, то есть оно работает намного, намного медленнее, чем могло бы работать.

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

На самом деле такие компании, как Apple и Microsoft, уже планируют, как сделать свои все еще самые однопоточные пользовательские интерфейсы многопоточными.Даже при описанном выше подходе однажды может возникнуть ситуация, когда пользовательский интерфейс сам по себе является узким местом.Фоновые процессы могут обрабатывать данные намного быстрее, чем пользовательский интерфейс может представить их пользователю или запросить у пользователя ввод.Сегодня многие фреймворки пользовательского интерфейса мало потокобезопасны, многие вообще не потокобезопасны, но это изменится.Последовательная обработка (выполнение одной задачи за другой) - умирающий дизайн, за параллельной обработкой (выполнение многих задач одновременно) - будущее.Просто посмотрите на графические адаптеры.Даже самая современная карта NVidia обладает жалкой производительностью, если посмотреть на скорость обработки в МГц / ГГц одного только графического процессора.Как получается, что он может выбивать дерьмо из процессоров, когда дело доходит до 3D-вычислений?Простой:Вместо вычисления одной точки полигона или одного пикселя текстуры за другим, он вычисляет многие из них параллельно (фактически целую кучу одновременно) и таким образом достигает пропускной способности, которая все еще заставляет процессоры плакать.Например.ATI X1900 (если называть и конкурента) имеет 48 шейдерных блоков!

Я думаю , что delayedresult это то, что вы ищете:

http://www.wxpython.org/docs/api/wx.lib.delayedresult-module.html

Смотрите демонстрацию wxpython для примера.

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

Ветки - Давайте воспользуемся простым двухслойным представлением (графический интерфейс, логика приложения).

Логическая работа приложения должна выполняться в отдельном потоке Python.Для асинхронных событий, которые должны распространяться до уровня GUI, используйте систему событий wx для публикации пользовательских событий.Публикация событий wx потокобезопасна, поэтому вы могли бы сделать это из нескольких контекстов.

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

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

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

Редактировать - Забыл упомянуть, что прелесть этого заключается в том, что можно полностью отделить логику приложения от кода GUI.Модульность помогает, если вы когда-нибудь решите использовать другой фреймворк или использовать версию приложения для командной строки.Для этого вам понадобится промежуточный диспетчер событий (уровень приложения -> GUI), который реализован на уровне GUI.

Работа с Qt/C++ для Win32.

Мы разделяем основные рабочие подразделения на различные процессы.Графический интерфейс выполняется как отдельный процесс и способен командовать / получать данные от "рабочих" процессов по мере необходимости.Прекрасно работает в современном многоядерном мире.

Этот ответ не относится к вопросу OP относительно Python, но является скорее мета-ответом.

Самый простой способ - это потоки.Однако не каждая платформа имеет упреждающую потоковую обработку (напримерBREW, некоторые другие встроенные системы) Если возможно, просто разделите работу и выполните ее в обработчике событий IDLE.

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

Я использую потоки, поэтому основной цикл событий GUI никогда не блокируется.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top