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

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

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

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

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

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 4

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

Ответ 9

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

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

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