Совет для преобразования большого монолитного однопоточного приложения к многопоточной архитектуре?

Основной продукт моей компании является большим монолитным приложением C++, используемым для обработки научных данных и визуализации. Его кодовая база возвращается, возможно, 12 или 13 лет, и в то время как мы поместили работу в обновление и поддержание ее (использование STL и Повышение - когда я присоединился, большинство контейнеров было пользовательским, например - полностью обновленный до Unicode и VCL 2010, и т.д.) существует одна остающаяся, очень значительная проблема: это является полностью однопоточным. Учитывая он - программа обработки данных и визуализации, это становится все большим количеством препятствия.

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

Поток данных программы мог бы пройти примерно так:

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

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

Я обращаюсь за советом о том, как приблизиться к изменению этого. Практические идеи. Возможно, вещи как:

  • шаблоны разработки для того, чтобы асинхронно запросить данные?
  • хранение большого количества объектов, таким образом, что потоки могут читать и записать безопасно?
  • обработка аннулирования наборов данных, в то время как что-то пытается считать его?
  • есть ли шаблоны и методы для этого вида проблемы?
  • что я должен просить, чтобы я не думал?

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

Конечная цель состоит в том, чтобы иметь полностью быстро реагирующую программу, где все поколение вычислений и данных сделано в других потоках, и UI является всегда быстро реагирующим. Мы не могли бы добраться там в единственном цикле разработки :)


Править: Я думал, что должен добавить пару большего количества деталей о приложении:

  • Это - 32-разрядное настольное приложение для Windows. Каждая копия лицензируется. Мы планируем сохранить это рабочим столом, локально-запущенным-приложением
  • Мы используем Причал (раньше Borland) Разработчик C++ 2010 для разработки. Это влияет на параллельные библиотеки, которыми мы можем пользоваться, так как большинство, кажется (?), записано для GCC или MSVC только. К счастью они активно разрабатывают его, и его поддержка стандартов C++ намного лучше, чем это раньше было. Компилятор поддерживает эти компоненты Повышения.
  • Его архитектура не является столь чистой, как это должно быть, и компоненты часто слишком сильно связываются. Это - другая проблема :)

Редактирование № 2: Спасибо за ответы до сих пор!

  • Я удивлен, что столько людей рекомендовало мультиархитектуру процессов (это - проголосовавший вершине ответ в данный момент), не многопоточность. Мое впечатление, это очень структура программы выхода Unix, и я ничего не знаю о том, как это разработано или работает. Действительно ли там хорошие ресурсы доступны об этом в Windows? Это находится действительно настолько распространено в Windows?
  • С точки зрения конкретных подходов к некоторым предложениям многопоточности, там шаблоны разработки для асинхронного запроса и потребления данных или ориентированных на многопотоковое исполнение или асинхронных систем MVP, или как разработать ориентированную на задачу систему, или статьи и книги и deconstructions после выхода иллюстрирование вещей, которые работают и вещи, которые не работают? Мы можем разработать всю эту архитектуру сами, конечно, но хорошо работать от того, что другие сделали прежде и знают что ошибки и ловушки избегать.
  • Одним аспектом, который не затронут ни в каких ответах, является проект, управляющий этим. Мое впечатление оценивает, сколько времени это возьмет и удержание контроль над проектом при выполнении чего-то столь неопределенного, как это может быть твердо. Это - одна причина, которая я после рецептов или практического совета кодирования, я предполагаю, чтобы вести и ограничить направление кодирования как можно больше.

Я еще не отметил ответ для этого вопроса - это не из-за качества ответов, которое является большим (и слова благодарности), но просто что из-за объема этого я надеюсь на большее количество ответов или обсуждения. Слова благодарности тем, кто уже ответил!

32
задан David 9 February 2010 в 05:30
поделиться

14 ответов

Итак, в вашем описании алгоритма есть подсказка относительно дальнейших действий:

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

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

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

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

15
ответ дан 27 November 2019 в 20:32
поделиться

Для начала создайте индекс:

CREATE INDEX messages_sender_by_day ON messages (sender, день);

(Вам, вероятно, не нужно включать «час» туда.)

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

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

-121--5044499-

Мы разработали vbscript 'launcher' для наших приложений доступа. Это то, что связано с в меню запуска пользовательских пк, и он делает следующее.

  • Он проверяет файл version.txt, расположенный в сетевом общем ресурсе, чтобы узнать, содержит ли он текст, отличный от локально сохраненной копии
  • Если текст отличается, он копирует mdb доступа и новый version.txt на жесткий диск пользователя.
  • Наконец, она запускает mdb в доступе

Для распространения обновления на ПК пользователя необходимо изменить текст в файле version.txt в сетевой общей папке.

Возможно, можно реализовать нечто подобное

-121--3643145-

Можно просто начать разбивать пользовательский интерфейс и рабочую задачу на отдельные потоки.

В методе рисования вместо непосредственного вызова getData () запрос помещается в очередь, защищенную от потоков. getData () выполняется в другом потоке, считывающем его данные из очереди. Когда поток getData выполнен, он сигнализирует главному потоку перерисовать область визуализации с его данными результата, используя синхронизацию потока для передачи данных.

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

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

6
ответ дан 27 November 2019 в 20:32
поделиться

Вот что я бы сделал ...

Я бы начал с вашего профилирования и увидел:

1) что медленное, а что горячее пути: 2) какие вызовы являются реентерабельными или глубоко вложенными

, вы можете использовать 1), чтобы определить, где есть возможность для ускорения и где начать поиск распараллеливания.

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

Я бы использовал хороший системный профилировщик и хороший профилировщик выборки (например, набор инструментов для перфорации Windows или параллельные представления профилировщика в Visual Studio 2010 Beta2 - оба они сейчас «бесплатны»).

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

Если у вас нет хорошего инструмента рефакторинга, такого как VisualAssist, купите его - оно того стоит. Если вы не знакомы с книгами по рефакторингу Майкла Фезерса или Кента Бека, подумайте о том, чтобы позаимствовать их. Я бы позаботился о том, чтобы мои рефакторинги хорошо покрывались модульными тестами.

Вы не можете перейти на VS (я бы порекомендовал продукты, над которыми я работаю, Библиотека асинхронных агентов и Библиотека параллельных шаблонов, вы также можете использовать TBB или OpenMP).

Что касается boost, я бы внимательно посмотрел на boost :: thread, библиотеку asio и библиотеку сигналов.

Я бы попросил помощи / совета / слуха, когда я застревал.

-Rick

2
ответ дан 27 November 2019 в 20:32
поделиться

Есть кое-что, о чем еще никто не говорил, но что довольно интересно.

Это называется будущее s. Будущее - это обещание результата ... давайте посмотрим на примере.

future<int> leftVal = computeLeftValue(treeNode); // [1]

int rightVal = computeRightValue(treeNode); // [2]

result = leftVal + rightVal; // [3]

Это довольно просто:

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

  2. Пока вычисляется leftVal , вы вычисляете rightVal .

  3. Вы складываете два, это может заблокировать, если leftVal еще не вычислено, и дождаться завершения вычисления.

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

См. Статью Херба Саттера о будущих s, они будут доступны в следующих C ++ 0x , но библиотеки уже доступны сегодня, даже если синтаксис, возможно, не такой красивый, как я могу заставить вас поверить;)

3
ответ дан 27 November 2019 в 20:32
поделиться

Похоже, у вас есть несколько разных проблем, которые можно решить с помощью параллелизма, но по-разному.

Повышение производительности за счет использования многоядерных архитектур ЦП

Вы не пользуетесь преимуществами многоядерных архитектур ЦП, которые становятся настолько распространенными. Распараллеливание позволяет разделить работу между несколькими ядрами. Вы можете написать этот код с помощью стандартных методов C ++ «разделяй и властвуй», используя «функциональный» стиль программирования, когда вы передаете работу отдельным потокам на этапе разделения. Шаблон Google MapReduce является примером этой техники. У Intel есть новая библиотека CILK , которая предоставляет компилятору C ++ поддержку таких методов.

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

Отделение операций графического интерфейса пользователя от операций с документом и размещение их в разных потоках позволяет повысить очевидную скорость отклика вашего приложения. Стандартные шаблоны проектирования Model-View-Controller или Model-View-Presenter - хорошее место для начала. Вам необходимо распараллелить их, указав, чтобы модель сообщала представлению об обновлениях, а не предоставляла представление о потоке, в котором документ вычисляет себя.Представление вызовет метод модели с просьбой вычислить конкретное представление данных, и модель будет информировать ведущего / контроллера об изменении информации или появлении новых данных, которые будут переданы представлению для обновления.

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

Очевидно, вам нужно будет использовать мьютексы (или критические секции), события и, возможно, семафоры, чтобы реализовать это. Некоторые из новых объектов синхронизации в Vista могут оказаться полезными, например, тонкая блокировка чтения-записи, переменные условия или новый API пула потоков. См. книгу Джо Даффи о параллелизме , чтобы узнать, как использовать эти основные методы.

3
ответ дан 27 November 2019 в 20:32
поделиться
$('#' + id).is(":checked")

Получается, если флажок установлен.

Для массива флажков с тем же именем можно получить список проверенных:

var $boxes = $('input[name=thename]:checked');

Затем, чтобы пройти через них и посмотреть, что проверено вы можете сделать:

$boxes.each(function(){
    // Do stuff here with this
});

Чтобы узнать, сколько проверено, вы можете сделать:

$boxes.length;
-121--1757502-

Мы разработали vbscript 'launcher' для нашего доступа apps. Это то, что связано с в меню «Пуск» пользовательских компьютеров и он делает следующее.

  • Он проверяет файл version.txt, расположенный в сетевой общей папке, чтобы проверить, содержит ли он текст, отличный от локально сохраненной копии
  • Если текст отличается, он копирует mdb доступа и новый version.txt на жесткий диск пользователя.
  • Наконец, запускается mdb в access

Для распространения обновления на ПК пользователя необходимо изменить текст в файле version.txt на сетевом общем ресурсе.

Возможно, можно реализовать нечто подобное

-121--3643145-

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

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

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

Тем временем...

У вас должно быть 2 полных копии этой готовой для отображения структуры. Во-первых, на что смотрит WM_PAINT сообщение. (назовите его cfd _ A ) Другое - это то, что вы передаете функции CookDataForDisplay (). (назовите его cfd _ B ). Функция CookDataForDisplay () выполняется в отдельном потоке и работает над построением/обновлением cfd _ B в фоновом режиме. Эта функция может занимать столько времени, сколько требуется, поскольку она не взаимодействует с дисплеем ни в одном пути. После возврата вызова cfd _ B будет самой последней версией структуры.

Теперь поменяйте местами cfd _ A и cfd _ B и InvalidateRect в окне приложения.

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

Итак, возвращаясь к вашему примеру.

  • В методе рисования вызывается метод GetData,часто сотни раз для сотен битов данных за одну операцию покраски

Это теперь 2 потока, метод покраски относится к cfd_A и выполняется на потоке пользовательского интерфейса. Тем временем cfd_B строится фоновым потоком с помощью вызовов GetData.

Быстрый и грязный способ сделать это -

  1. Возьмите текущий код WM_PAINT, поместите его в функцию PaintIntoBitmap ().
  2. Создайте битовую карту и контроллер домена памяти, это cfd_B.
  3. Создайте поток и передайте его cfd_B и вызовите PaintIntoBitmap ()
  4. После завершения этого потока поменяйте местами cfd_B и cfd_A

Now новый метод WM_PAINT просто берет предварительно визуализированное растровое изображение в cfd_A и выводит его на экран. Теперь пользовательский интерфейс отключен от бэкэнд-функции GetData ().

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

7
ответ дан 27 November 2019 в 20:32
поделиться

Перед вами стоит большая задача. Передо мной стояла аналогичная задача - 15-летняя монолитная однопоточная кодовая база, не использующая преимущества многоядерности и т. Д. Мы приложили много усилий, пытаясь найти конструкцию и решение, которые были бы работоспособными и работали бы.

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

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

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

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

Это может показаться взломом. А может быть, так оно и есть. Но он даст вам то, что вам нужно, не подвергая стабильность и производительность вашей системы такому большому риску. Плюс есть скрытые преимущества. Во-первых, невидимые копии ядра вашего приложения будут иметь доступ к своему собственному пространству виртуальной памяти, что упрощает использование всех ресурсов системы. Он также хорошо масштабируется. Если вы работаете на двухъядерном двигателе, вы можете создать 2 копии своего двигателя. 32 ядра? 32 экз. Вы уловили идею.

16
ответ дан 27 November 2019 в 20:32
поделиться

Трудно дать вам правильные рекомендации. Но ...

По моему мнению, самый простой выход - это преобразовать ваше приложение в ActiveX EXE, поскольку COM имеет встроенную поддержку Threading и т. Д., Ваша программа автоматически станет многопоточным приложением. Конечно, вам придется внести в свой код немало изменений. Но это самый короткий и безопасный путь.

Я не уверен, но, вероятно, библиотека RichClient Toolset поможет вам. На сайте автор написал:

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

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

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

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

При разработке этого программного обеспечения мы столкнулись с той же проблемой, что вы проиллюстрировали здесь. Чтобы решить эту проблему, мы преобразовали приложение в ActiveX EXE, и мы преобразовали все те части, которые должны выполняться параллельно, в библиотеки DLL ActiveX. Мы не использовали для этого какие-либо сторонние библиотеки!

HTH

-3
ответ дан 27 November 2019 в 20:32
поделиться

Если бы я тратил деньги на развитие, я бы начал с общей картины:

  1. Чего я надеюсь достичь, и сколько я потрачу на это, и как я буду двигаться дальше? (Если ответ на этот вопрос таков, мое приложение будет работать на 10% лучше на четырехъядерных ПК, и я мог бы достичь того же результата, потратив на 1000 долларов больше на ПК клиента и потратив на 100000 долларов меньше в этом году на НИОКР, тогда я бы пропустил все усилия).

  2. Почему я использую многопоточность вместо массового параллельного распределения? Я действительно считаю, что потоки лучше процессов? Многоядерные системы также неплохо запускают распределенные приложения. И у систем на основе процессов передачи сообщений есть некоторые преимущества, которые выходят за рамки преимуществ (и затрат!) Потоковой передачи. Стоит ли рассматривать процессный подход? Следует ли мне рассматривать фон, работающий исключительно как службу, и графический интерфейс переднего плана? Поскольку мой продукт заблокирован и лицензирован, я думаю, что услуги мне (поставщику) вполне подойдут. Кроме того, разделение материала на два процесса (фоновая служба и передний план) может привести к тому, что произойдет переписывание и изменение архитектуры, которые мне, возможно, не пришлось бы делать, если бы я просто добавил потоки в свой микс.

  3. Это просто для того, чтобы вы подумали: что, если бы вы переписали его как службу (фоновое приложение) и графический интерфейс, потому что на самом деле это было бы проще, чем добавление потоков, без добавления сбоев, взаимоблокировок и условий гонки?

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

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

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

Интересно, похожи ли проблемы с «дизайном» этого приложения C ++ Builder на болезнь моего приложения Delphi «RAD Spaghetti». Я обнаружил, что полный рефакторинг / перезапись (более года на одно основное приложение, для которого я это сделал), были для меня минимальным количеством времени, чтобы справиться с «случайной сложностью» приложения. И это было без подбрасывания идеи «по возможности». Я обычно пишу свои приложения с потоками только для последовательной связи и обработки сетевых сокетов. И, возможно, странная «очередь рабочих потоков».

Если в вашем приложении есть место, где вы можете добавить ОДИН поток, чтобы проверить, что происходит, я бы поискал основную «рабочую очередь», и я бы создал экспериментальную ветвь управления версиями, и я бы узнал, как мой код работает взломав его в экспериментальной ветке. Добавьте эту тему. И посмотрите, где вы проведете свой первый день отладки. Тогда я мог бы просто оставить эту ветку и вернуться к своему стволу, пока боль в моей височной доле не утихнет.

Уоррен

3
ответ дан 27 November 2019 в 20:32
поделиться

Судя по комментариям, вы ожидаете многого. Вы не собираетесь переходить с минут на миллисекунды из-за многопоточности. Максимум, на что вы можете надеяться, - это разделить текущее количество времени на количество ядер. При этом вам немного повезло с C ++. Я написал высокопроизводительные многопроцессорные научные приложения, и вы хотите найти самый беспорядочно параллельный цикл, который вы можете найти. В моем научном коде самая тяжелая часть вычисляет от 100 до 1000 точек данных. Однако все точки данных можно рассчитать независимо от других. Затем вы можете разделить цикл с помощью openmp. Это самый простой и эффективный способ. Если ваш компилятор не поддерживает openmp, вам будет очень сложно переносить существующий код. С openmp (если вам повезет) вам, возможно, придется добавить пару #pragmas, чтобы повысить производительность в 4-8 раз. Вот пример StochFit

1
ответ дан 27 November 2019 в 20:32
поделиться

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

http://www.freevbcode.com/ShowCode.Asp?ID=1287

Надеюсь, это поможет.

0
ответ дан 27 November 2019 в 20:32
поделиться
1
ответ дан 27 November 2019 в 20:32
поделиться

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

ШАГ 1 - Отзывчивый графический интерфейс

Мы можем предположить, что изображение, которое вы создаете, содержится в холсте TImage. Вы можете поместить простой TTimer в вашу форму и написать код, подобный этому:

if (CurrenData.LastUpdate>CurrentUpdate)
    {
    Image1->Canvas->Draw(0,0,CurrenData.Bitmap);
    CurrentUpdate=Now();
    }

OK! Я знаю! Это немного грязно, но это быстро и просто. Суть в том, что:

  1. Вам нужен объект, который создается в главном потоке
  2. Объект копируется в нужную вам форму, только когда это необходимо и безопасным способом (возможно, нужна лучшая защита для Bitmap, но для простоты...)
  3. Объект CurrentData - это ваш реальный проект, однопоточный, который производит изображение

Теперь у вас быстрый и отзывчивый GUI. Если ваш алгоритм медленный, то обновление будет медленным, но пользователь никогда не подумает, что ваша программа замерла.

ШАГ 2 - Многопоточность

Я предлагаю вам реализовать класс, подобный следующему:

SimpleThread.h

typedef void (__closure *TThreadFunction)(void* Data);

class TSimpleThread : public TThread
{
public:
    TSimpleThread( TThreadFunction _Action,void* _Data = NULL, bool RunNow = true );
    void AbortThread();

    __property Terminated; 

protected:
    TThreadFunction ThreadFunction;
    void*           Data;

private:
    virtual void __fastcall Execute() { ThreadFunction(Data); };
};

SimpleThread.c

TSimpleThread::TSimpleThread( TThreadFunction _Action,void* _Data, bool RunNow)
             : TThread(true), // initialize suspended
               ThreadFunction(_Action), Data(_Data)
{
FreeOnTerminate = false;
if (RunNow) Resume();
}

void TSimpleThread::AbortThread()
{
Suspend(); // Can't kill a running thread
Free();    // Kills thread
}

Давайте объясним. Теперь в вашем простом классе с потоком вы можете создать объект, подобный этому:

TSimpleThread *ST;
ST=new TSimpleThread( RefreshFunction,NULL,true);
ST->Resume();

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

0
ответ дан 27 November 2019 в 20:32
поделиться
  1. Не пытайтесь использовать все в старом приложении в многопоточном режиме. Многопоточность ради того, чтобы сказать, что она многопоточна, - это пустая трата времени и денег. Вы создаете приложение, которое что-то делает, а не памятник самому себе.
  2. Профилируйте и изучите свои потоки выполнения, чтобы выяснить, где приложение проводит большую часть своего времени. Профилировщик - отличный инструмент для этого, но это просто пошаговое выполнение кода в отладчике. Вы находите самое интересное в случайных прогулках.
  3. Отделите пользовательский интерфейс от длительных вычислений. Используйте методы межпотоковой связи для отправки обновлений пользовательского интерфейса из вычислительного потока.
  4. В качестве побочного эффекта №3: ​​тщательно подумайте о повторном входе: теперь, когда вычисления выполняются в фоновом режиме, и пользователь может перемещаться по пользовательскому интерфейсу, какие элементы пользовательского интерфейса следует отключить, чтобы предотвратить конфликты с фоном. операция? Разрешить пользователю удалять набор данных во время вычислений с этими данными, вероятно, является плохой идеей. (Смягчение: при вычислении создается локальный моментальный снимок данных) Имеет ли смысл для пользователя запускать несколько вычислительных операций одновременно? При правильном обращении это может быть новой функцией, которая поможет рационализировать усилия по переработке приложения. Если проигнорировать это будет катастрофа.
  5. Определите конкретные операции, которые могут быть помещены в фоновый поток. Идеальным кандидатом обычно является отдельная функция или класс, который выполняет много работы (для выполнения требуется «много времени» - более нескольких секунд) с четко определенными входами и выходами, которые не используют глобальные ресурсы и делают не прикасайтесь к пользовательскому интерфейсу напрямую.Оценивайте и расставляйте приоритеты кандидатов в зависимости от того, сколько работы потребуется для модернизации до этого идеала.
  6. С точки зрения управления проектом, делайте шаг за шагом. Если у вас есть несколько операций, которые являются сильными кандидатами на перемещение в фоновый поток, и они не взаимодействуют друг с другом, они могут быть реализованы параллельно несколькими разработчиками. Тем не менее, было бы неплохо, если бы все сначала участвовали в одном преобразовании, чтобы все понимали, что нужно искать, и устанавливать свои шаблоны для взаимодействия с пользовательским интерфейсом и т. Д. Проведите расширенное собрание доской, чтобы обсудить дизайн и процесс извлечения одного функция в фоновый поток. Пойдите и реализуйте это (вместе или раздайте кусочки отдельным людям), а затем соберите все вместе и обсудите открытия и болевые точки.
  7. Многопоточность - это головная боль, и она требует более тщательного обдумывания, чем прямое кодирование, но разделение приложения на несколько процессов создает гораздо больше головной боли, IMO. Поддержка потоковой передачи и доступные примитивы хороши в Windows, возможно, лучше, чем на некоторых других платформах. Используй их.
  8. В общем, не делайте больше, чем нужно. Легко переборщить с реализацией и усложнить проблему, добавив в нее больше шаблонов и стандартных библиотек.
  9. Если никто в вашей команде раньше не выполнял многопоточную работу, выделите время, чтобы нанять эксперта, или средства, чтобы нанять его в качестве консультанта.
8
ответ дан 27 November 2019 в 20:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: