C++ Порядок выполнения потоков в пуле потоков
Кто-нибудь знает о реализации пула потоков C++, которая допускает как параллельную многопоточность (как типичный пул потоков), но также допускает последовательный последовательный порядок выполнения. Я потратил несколько дней, пытаясь заставить это работать, изменяя следующий пул потоков, но я не могу заставить его работать. Я рассмотрел методы, используемые Intel TBB, а также, возможно, использовал концепции PPL от Microsoft (ее библиотека асинхронных агентов выглядит многообещающе) - обе из которых имеют ориентированные на задачи методы для достижения вышеупомянутых - К сожалению, однако, эти решения будут не работает моя цель PowerPC Linux встроенная цель.
РЕДАКТИРОВАТЬ Я собрал демо-версию coliru с источником, который создает граф потоков, а также показывает хороший пример scheduler_loop, где теоретически можно было бы ожидать завершения потоков. В коде также показан UtlThreadPool с двумя потоками, где я передаю его с помощью параллельных задач - однако это "кормление" не совсем корректно и потребуется небольшая работа для прохождения через узлы.
Структура данных, которую я использую для построения графика выполнения, показана ниже. Он использует структуру данных PriorityNode. Эта структура по сути представляет собой связанный список PriorityNodes, каждый из которых содержит вектор задач PriorityLevel, которые могут выполняться одновременно, и указатель на следующий PriorityNode, который указывает потоки, которые будут выполняться последовательно. После того, как они ВСЕ завершены, если член mNextNode не является nullptr, то это должно быть запланировано для запуска в пуле потоков (и так далее, пока mNextNode не станет nullptr. Последовательность по этому связанному списку PriorityNodes - это то, как я бы хотел, чтобы поток Пул для последовательности через свои потоки. У PriorityNode есть оператор вставки, который обычно производит вывод следующим образом. (Это будет означать, что 1A1 может быть запущен одновременно с 1A2, и когда оба этих потока завершат, следующий PriorityNode разрешит 1B1, 1B2, 1B3 и 1B4 для одновременного запуска - на всех потоках, доступных пулу.
1A1
1A2
+-1B1
+-1B2
+-1B3
+-1B4
Похоже, что самое близкое, что у меня есть, к решению этой проблемы - опять же обратите внимание, что это специфично для Intel, и я нахожусь на мощном ПК, это Intel TBB - вот пример, который они используют для порядка последовательного выполнения.
/**
* Branch representing fundamental building block of
* a priority tree containing szPriority entries.<p>
*
* Each priority tree struct contains a vector of concurrent
* priorities that can be scheduled to run in the thread pool -
* note that the thread pool must have no entries associated
* with the current channel running before enqueueing these
* tasks. The application must wait for the thread pool to
* complete these tasks before queuing up the dependent tasks
* described in the mNextNode smart pointer. If mNextNode is
* unassigned (nullptr), then we have reached the end of the
* tree.
*/
struct PriorityNode {
explicit PriorityNode(
const std::vector<PriorityLevel>& rConcurrent,
const std::shared_ptr<PriorityNode>& rNext = std::shared_ptr<PriorityNode>(),
const size_t& rDepth = 0)
: mConcurrent(rConcurrent)
, mNextNode(rNext)
, mDepth(rDepth)
{}
/**
* Stream insert operator<p>
*
* @param os [in,out] output stream
* @param rhs [in] PriorityLevel to send to the output
* stream.
*
* @return a reference to the updated stream
*/
inline friend std::ostream& operator << (
std::ostream& os, const PriorityNode& rhs) {
// indent 2 spaces per depth level
std::string indent = rhs.mDepth > 0 ?
(std::string("+") +
std::string((rhs.mDepth * 2) - 1, '-')) :
std::string();
// print out the concurrent threads that
// can be scheduled with the thread pool
for (const auto& next : rhs.mConcurrent) {
os << indent << next << std::endl;
}
// print the dependent priorities that can only
// be scheduled when the concurrent ones are finished
if (rhs.mNextNode) {
os << *rhs.mNextNode << std::endl;
}
return os;
}
// these are all equivalent thread priorities
// that can be run simultaneously
std::vector<PriorityLevel> mConcurrent;
// these are concurrent threads that must be AFTER all
// mConcurrent tasks have completed (exiting the thread pool)
std::shared_ptr<PriorityNode> mNextNode;
// recursion depth
size_t mDepth;
};
1 ответ
Почему бы просто не использовать TBB на PowerPC? Это очень портативная библиотека, разработанная для кроссплатформенности и практичности и я слышал, что он портируется на BlueGen сообществом открытого кода TBB. Вы можете задать их на форуме Intel TBB, например, возродив эту ветку форума.
Intel не распространяет двоичные файлы PowerPC для TBB, но вы можете попробовать собрать его из источников, просто
сделать TBB
Смотрите также эти патчи сообщества.
Если кто-то все еще ищет это, посмотрите репо здесь - https://github.com/hirak99/ordered_thread_pool
В основном этим вы можете заменить такой код -
while (...) {
std::cout << CostlyFn(input)) << std::endl;
}
В это -
OrderedThredPool<std::string> pool{10, 1};
while (...) {
pool.Do(
[&input] { return CostlyFn(input); },
[](const std::string& out) {
std::cout << out << std::endl;
});
}
И он будет исполняться по порядку.