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

Я ищу шаблон проектирования, который бы соответствовал дизайну моего приложения.

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

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

Я не хочу создавать несколько индикаторов выполнения. Вместо этого я хотел бы иметь один индикатор выполнения, который информирует о глобальном прогрессе. На данный момент я могу думать о MVC и Observer/Observable, но это немного размыто :) Может быть, кто-нибудь может указать мне правильное направление, спасибо.


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


Ответы (4)


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

Алгоритм состоял из 6 различных шагов. Каждый шаг имел временные характеристики, которые серьезно зависели от: а) обрабатываемых базовых данных, не только «количества» данных, но также и «типа» данных, и б) 2 шага очень хорошо масштабировались с увеличением числа процессоров, 2 шага выполнялись в 2 потока, а 2 шага были фактически однопоточными.

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

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

f1(..) + f2(..) + f3(..) + f4(..) + f5(..) + f6(..) = общее время выполнения в миллисекундах

Теперь, имея эту информацию, вы можете точно знать, какой процент от общего времени выполнения должен занимать каждый шаг. Теперь, если вы говорите, что шаг 1 должен занимать 40% времени выполнения, вам в основном нужно выяснить, как генерировать 40 событий 1% из этого алгоритма. Скажем, цикл for обрабатывает 100 000 элементов, вы, вероятно, могли бы сделать:

for (int i = 0; i < numItems; i++){
     if (i % (numItems / percentageOfTotalForThisStep) == 0) emitProgressEvent();
     .. do the actual processing ..
}

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

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

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

person krosenvold    schedule 14.02.2009

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

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

Таким образом, пока ваша фактическая работа выполняется в отдельных потоках, соответствующий объект Operation в «основном» потоке постоянно обновляется/уведомляется о ходе работы своего рабочего. Индикатор выполнения может обновляться соответствующим образом, сопоставляя общее «ожидаемое» время операций с его общим количеством, а общее время «прогресса» операций с его текущим ходом любым способом, который имеет смысл для вашей структуры индикатора выполнения.

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

person erikprice    schedule 14.02.2009

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

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

Извините, я не могу предложить более конкретный дизайн, это просто общий совет. :)

person Assaf Lavie    schedule 14.02.2009

Придерживайтесь Observer/Observable для такого рода вещей. Некоторый объект наблюдает за потоками обработки различных серий и сообщает о состоянии, обновляя сводную панель.

person S.Lott    schedule 14.02.2009