Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.119.123.252] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Всем привет.
Есть вот такой набросок псевдокода: class IModuleUpgrader { virtual void SetUpgradeCode() = 0; virtual void SetVersion() = 0; virtual void UpgradeModule(string modulename) = 0; } struct IsmUpdater : public IModuleUpgrader { void UpgradeModule(string modulename) override { parser.ProcessUpgrade(); parser.Save(); } void SetUpgradeCode() override {} void SetVersion() override {} private: XMLParser parser; }; struct WixUpdater : public IModuleUpgrader { void UpgradeModule(string modulename) override { parser.ProcessUpgrade(); parser.Save(); } void SetUpgradeCode() override {} void SetVersion() override {} private: XMLParser parser; }; ... struct UpgradeTool { void AddUpgradeTool(const std::wstring& ext, std::unique_ptr<IModuleUpgrader>&& upgrader) { m_scaner.AddFilter(ext); m_upgraders.insert(make_pair(ext,std::move(upgrader))); } //! Producer выполняется в отдельном потоке void Scann() { m_scanner.ScannAndPutResultToContainer(m_container); } //! Consummer может выполнятся в отдельных разных потоках void Upgrade() { while(true) { auto module = m_container.pop(); auto ext = GetModuleType(module); if(m_upgraders.has(ext)) { auto upgrader = m_upgraders[ext]; upgrader->SetUpgradeCode(...); upgrader->SetVersion(...); upgrader->Upgrade(); } } } private: FileScanner m_scanner; ThreadSafeQueue<std::wstring> m_container; std::map<std::wstring, std::unique_ptr<IModuleUpgrader>> m_upgraders; } По задумке мы пихаем расширения нужных нам файлов в класс UpgradeTool, и класс, который может их обрабатывать. Дальше запускается отдельный поток, в котором ищутся все файлы с заданным расширением и файлы эти кладутся в отдельную потокобезопасную очередь, потом запускается несколько отдельных потоков, и каждый отдельный поток начинает обрабатывать свой файл. Но тут есть косяк: Если я вызову AddUpgradeTool(".ext", new IsmUpdater ); То у меня будет 1 объект класса IsmUpdater, а мне нужно чтоб было столько, сколько потоков, но я не могу понять как это сделать. Вот например приходила такая идея: template<typename _Type = IModuleUpgrader> struct ProxyModuleUpgrader { using TModuleUpgrader = _Type; }; std::map<std::wstring, std::unique_ptr<ProxyModuleUpgrader<>>> m_upgraders; //! Где то : upgrade.AddUpgradeTool<IsmUpdater>(L".ext", new IsmUpdater) //! Внутри апгрейда void UpgradeTool::Upgrade() { while(true) { auto module = m_container.pop(); auto ext = GetModuleType(module); if(m_upgraders.has(ext)) { auto upgrader = m_upgraders[ext]::TModuleUpgrader; upgrader->SetUpgradeCode(...); upgrader->SetVersion(...); upgrader->Upgrade(); } } } Но эта идея тоже провалилась. Может быть есть какой то шаблон или прием для решения такой задачи? Я что то не могу сообразить как же так лучше сделать то |
Сообщ.
#2
,
|
|
|
Цитата Wound @ как же так лучше сделать то А задача-то какая? Многопоточная обработка файлов по расширениям и/или по группам расширений? |
Сообщ.
#3
,
|
|
|
Цитата JoeUser @ Многопоточная обработка файлов по расширениям и/или по группам расширений? В одном потоке запускается хрень(FileScanner), которая ищет все файлы в директории и поддиректориях по указанным расширениям, и пихается путь к найденному файлу в очередь, а потом запускаются несколько потоков, которые должны эту самую очередь выгребать и обновлять каждый файл, в зависимости от типа файла. Один файл может обновлятся достаточно долгое время(от секунды до минуты например). Для каждого потока должен создаваться отдельный объект класса, который умеет работать с файлом данного типа. Т.е. один класс на несколько потоков - не варик вообще. |
Сообщ.
#4
,
|
|
|
Цитата Wound @ Вот например приходила такая идея: template<typename _Type = IModuleUpgrader> struct ProxyModuleUpgrader { using TModuleUpgrader = _Type; }; std::map<std::wstring, std::unique_ptr<ProxyModuleUpgrader<>>> m_upgraders; //! Где то : upgrade.AddUpgradeTool<IsmUpdater>(L".ext", new IsmUpdater) Можно сделать например так std::map<std::wstring, std::function<std::unique_ptr<IModuleUpgrader>()>> upgraders; ..... upgraders.emplace(L".ext", []{return std::make_unique<IsmUpdater>()}); upgraders.emplace(L".ext2", []{return std::make_unique<WixUpdater>()}); .... auto it = upgraders.find(ext); if (it == upgraders.end()) return; auto upgrader = it->second(); |
Сообщ.
#5
,
|
|
|
Да примерно так и сделал, только добавил в интерфейс IModuleUpgrader новый чистовиртуальный метод -> virtual std::unique_ptr<IModuleUpgrader> CreateNewInstance() const = 0;
А уж потомки реализуют его как у тебя в лямбде. Хотя может твой способ и лучше, не нужно писать лишнего метода в интерфейсе. Спасибо. Добавлено Вопрос решен. |
Сообщ.
#6
,
|
|
|
Цитата Wound @ Да примерно так и сделал, только добавил в интерфейс IModuleUpgrader новый чистовиртуальный метод -> virtual std::unique_ptr<IModuleUpgrader> CreateNewInstance() const = 0; А уж потомки реализуют его как у тебя в лямбде. Его точно не реализуют. Чтобы вызвать виртуальный метод в интерфейсе, нужно сначала создать экземплляр кдасса который реализует этот интерфейс. Он будет через этот метод копировать себя, и больше ничего не делать, вряд ли ты станешь вызывать у него другие методы. Зачем для этого держать отдельный объект? Достаточно указателя на функцию или std::function. |
Сообщ.
#7
,
|
|
|
Цитата Олег М @ Его точно не реализуют. Чтобы вызвать виртуальный метод в интерфейсе, нужно сначала создать экземплляр кдасса который реализует этот интерфейс. Он будет через этот метод копировать себя, и больше ничего не делать, вряд ли ты станешь вызывать у него другие методы. Зачем для этого держать отдельный объект? Достаточно указателя на функцию или std::function. Фишка в том, что интерфейс там появился для того, чтобы внутри void Upgrade() вызвать соответствующие методы: void Upgrade() { while(true) { auto module = m_container.pop(); auto ext = GetModuleType(module); if(m_upgraders.has(ext)) { auto upgrader = m_upgraders[ext]; <------ Вот тут вместо auto должна быть копия объекта IsmUpdater или WixUpdater или еще чего то. upgrader->SetUpgradeCode(...); upgrader->SetVersion(...); upgrader->Upgrade(); } } } Для этого я создал интерфейс, и добавил функцию регистрации: void UpgradeTool::AddUpgradeTool(std::wstring ext, std::unique_ptr<Msi::IModuleUpgrader>&& upgrader) { m_scanner.AddExtension(ext); m_upgraders.insert(std::make_pair(std::move(ext), std::move(upgrader))); } Теперь в классах IsmUpdater и WixUpdater реализовал чистовиртуальную функцию интерфейса IModuleUpgrader: std::unique_ptr<IModuleUpgrader> ISMFileUpdater::CreateNewInstance() const { std::cout << "ISMFileUpdater" << std::endl; return std::make_unique<ISMFileUpdater>(); } Теперь в функцию потока void Upgrade() я могу написать так: void Upgrade() { while(true) { auto module = m_container.pop(); auto ext = GetModuleType(module); if(m_upgraders.has(ext)) { //auto upgrader = m_upgraders[ext]; <------ Вот тут вместо auto должна быть копия объекта IsmUpdater или WixUpdater или еще чего то. std::unique_ptr<Msi::IModuleUpgrader> ptr = m_upgraders[ext]; auto upgrader = ptr->CreateNewInstance(); upgrader->LoadModule(L"test.ism"); upgrader->SetUpgradeCode(...); upgrader->SetVersion(...); upgrader->Upgrade(); } } } А регистрировать свои классы вот так: UpgradeTool tool; tool.AddUpgradeTool(L".ism", std::make_unique<Msi::ISMFileUpdater>()); Это так сейчас сделано, если делать как ты написал с указателем на функцию, то ведь придется в функцию регистрации передавать лямбда выражение целое? Что то типа: UpgradeTool tool; tool.AddUpgradeTool(L".ism", []{return std::make_unique<IsmUpdater>()}); Добавлено Ну в принципе я так и переписал. Просто меня смущает что нужно передавать лямбду в параметр функции. Слишком запутанно выходит. Хотя может и нет. |
Сообщ.
#8
,
|
|
|
Wound, честно говоря, я бы начал немного раньше планировать разработку. А именно с анализа предметной области. Хотя бы с одного вопроса "какие ресурсы компа в потенциале в процессе обработки будут более востребованы?". Если ответ "а хрен его спрогнозирует" - тогда выбираем "классику". А именно, число потоков-обработчиков = N+1, где N-количество ядер проца. А вот, к примеру, если дисковых операций тьма, а математика не только лишь все имена файлов обрабатывает, мало какие данные в файлах обрабатываются (L) Тут свое построение нужно делать.
Одно понятно, сущностей четыре: Две очевидных стратегии: Ну и конечно же основной вопрос - если прога одноразовая, используем стратегию намба уан и не паримся ни разу. Все выше - мое ИМХО. В качестве бонуса - архивчик, в котором есть полезные книжки по твоему топику, может быть поможет : |
Сообщ.
#9
,
|
|
|
Цитата JoeUser @ Wound, честно говоря, я бы начал немного раньше планировать разработку. А именно с анализа предметной области. Хотя бы с одного вопроса "какие ресурсы компа в потенциале в процессе обработки будут более востребованы?". Если ответ "а хрен его спрогнозирует" - тогда выбираем "классику". А именно, число потоков-обработчиков = N+1, где N-количество ядер проца. А вот, к примеру, если дисковых операций тьма, а математика не только лишь все имена файлов обрабатывает, мало какие данные в файлах обрабатываются (L) Тут свое построение нужно делать. Да ресурсов не много нужно. Есть куча инсталяционных файлов Installshield и Wix(*.ism, *.wix), они представляют из себя XML, в них хранится вся информация, из которой потом компилируются инсталяционные пакеты Setup.exe/*.cab. Инсталяционных файлов много, ну штук под 30 примерно, и есть куча разных версий, которым никто не настраивал Major upgrade, А чтоб Major Upgrade работал, нужно в каждом файле сгенерировать новый ProductCode, потом нужно задать ProductVersion, И прописать все это в таблицу Upgrade - выставить нужные версии. Руками все это делать муторно и долго, и я задолбался. В итоге решил автоматизировать все это дело. Чтобы прога сама искала все нужные файлы и сама апгрейдила все что нужно во время компиляции, тогда даже когда сделают новую ветку, мне не нужно будет там настраивать Major Upgrade. В итоге я написал XML парсер, который загружает только нужные для апгрейда части, чтобы жрало поменьше памяти, написал класс который апгрейдид все это, и хочу теперь воссоединить это в одно. Так как файлов много, решил что каждый файл будет апгрейдится в отдельном потоке. Сколько для этого нужно потоков пока не знаю, но думаю юзать фьючерсы, там вроде они умные и сами умеют распределять ресуосы системы. Так что конкретно число потоков мне пока не важна. У меня проблема была в том, чтобы в отдельном потоке была отдельная копия класса-апгрейдера. Но вроде уже решилась. |
Сообщ.
#10
,
|
|
|
Wound, Киля, самый главный вопрос - задача разовая или перманентная по времени?
Добавлено Цитата Wound @ фьючерсы, там вроде они умные и сами умеют распределять ресуосы системы Это заблуждение! Распределять ресурсы умеет ОС. Программер может помочь, зная специфику обработки. |
Сообщ.
#11
,
|
|
|
Цитата JoeUser @ Wound, Киля, самый главный вопрос - задача разовая или перманентная по времени? Нет, она не одноразовая, она по сути много разовая и умеет работать с любыми инсталяционными файлами Installshield. Цитата JoeUser @ Это заблуждение! Распределять ресурсы умеет ОС. Программер может помочь, зная специфику обработки. Ну у Мейерса как раз и написано чтобы отдавали предпочтение фьючерсам, так как они шибко умные и сами знают нужно ли порождать поток или исполнять в текущем, а так же все тонкости балансировщика они тоже знают. |
Сообщ.
#12
,
|
|
|
Цитата Wound @ Цитата JoeUser @ Wound, Киля, самый главный вопрос - задача разовая или перманентная по времени? Нет, она не одноразовая, она по сути много разовая и умеет работать с любыми инсталяционными файлами Installshield. Тогда, действительно, задача интересна и стоит усилий! Цитата Wound @ Цитата JoeUser @ Ну у Мейерса как раз и написано чтобы отдавали предпочтение фьючерсам, так как они шибко умные и сами знают нужно ли порождать поток или исполнять в текущем, а так же все тонкости балансировщика они тоже знают.Это заблуждение! Распределять ресурсы умеет ОС. Программер может помочь, зная специфику обработки. Философское отступление. Несколько лет назад у меня спала "пелена из глаз". До этого, в течении многого времени, я считал себя заядлым агностиком. Многие вааще не понимали о чем речь. Но пришло осознание - я не агностик или атеист, я - неверующий. Даже религия тут не при чем! Я не верю ваще, ни во что. Полностью. Я пытаюсь воспроизвести и оценить. Гадил я с Пизанской башни на аксиомы "аксакалов". Но ... приду и проверю ... это я кидаю камень в сторону Меерса Есть "логика", есть высказывания Меерса, есть куча методик шкальных оценок достоверности. Ну и есть чуйка))) Поверил во "фьючерсы" - это не смертельно! Возьми вычлени репрезентативную выборку результатов синтетических тестов на своих данных - и ты поймешь. Нужно ли слепо идти за Мейерсом, или эффективнее просто прислушиваться, приглядоваться, записывать ходы... E2-E4? |
Сообщ.
#13
,
|
|
|
Цитата JoeUser @ Есть "логика", есть высказывания Меерса, есть куча методик шкальных оценок достоверности. Ну и есть чуйка))) Поверил во "фьючерсы" - это не смертельно! Возьми вычлени репрезентативную выборку результатов синтетических тестов на своих данных - и ты поймешь. Нужно ли слепо идти за Мейерсом, или эффективнее просто прислушиваться, приглядоваться, записывать ходы... E2-E4? Дело в том, что фьючерсы дают чуть больше функционала, чем потоки. Например, если внутри потока сгенерируется исключение, то ты его просто не словишь, с фьючерсами - легко обработать этот вариант, так же можно получить возвращаемое потоком значение. Ну и вывод, взято у Майерса: Цитата Следует запомнить • API std::thrеаd не предлагает способа непосредственного получения возвращаемых значений из асинхронно выполняемых функций, и, если такие функции генерируют исключения, программа завершается. • Программирование на основе потоков требует управления вручную исчерпанием потоков, превышением подписки, балансом загрузки и адаптацией к новым платформам. • Программирование на основе задач с помощью std : : async со стратегией запуска по умолчанию решает большинство перечисленных проблем вместо вас. Вообще у него вроде понятно расписано, чтобы знать что как работает, а не верить |
Сообщ.
#14
,
|
|
|
Цитата Wound @ Вообще у него вроде понятно расписано, чтобы знать что как работает, а не верить Вот и я говорю - написано одно, а так ли это на самом деле? Я понимаю, когда в реализацию компилятора/линкера вводят особенности архитектуры. Но вот на автомате предусмотреть особенности конкретной модели проца - это совсем другое. |
Сообщ.
#15
,
|
|
|
Цитата JoeUser @ Вот и я говорю - написано одно, а так ли это на самом деле? Я понимаю, когда в реализацию компилятора/линкера вводят особенности архитектуры. Но вот на автомате предусмотреть особенности конкретной модели проца - это совсем другое. При прочих равных фьючерсы более предпочитетельнее, чем потоки. А пилить какие то узкие места в многопоточности под конкретные модели процессора у меня задачи не стоит. |