【问题标题】:C++ Thread execution order in a thread poolC++线程池中的线程执行顺序
【发布时间】:2014-09-26 16:43:27
【问题描述】:

有谁知道 C++ 线程池实现,它既允许并行线程(如典型的线程池),又允许背靠背串行执行顺序。我花了几天时间尝试通过修改以下thread pool 来完成这项工作,但我似乎无法使其工作。我研究了英特尔 TBB 使用的技术,并且我研究了可能使用微软 PPL 中的概念(它的异步代理库看起来很有前途)——两者都有面向任务的技术来实现上述目标——但是不幸的是,这些解决方案将我的目标 PowerPC linux 嵌入式目标不起作用。

编辑我将一个实时 coliru demo 与生成线程图的源放在一起 - 并且还展示了一个很好的 scheduler_loop 示例,理论上可以等待线程去完成。该代码还显示了一个带有 2 个线程的 UtlThreadPool,我在其中为它提供并发任务 - 但是“提供”并不完全正确,需要做一些工作才能遍历节点。

我用来制作执行图的数据结构如下所示。它使用 PriorityNode 数据结构。这个结构本质上是一个 PriorityNode 的链表,每一个都包含一个可以并发运行的 PriorityLevel 任务的向量和一个指向下一个 PriorityNode 的指针,该指针指示之后要串行运行的线程。一旦这些都完成了,如果 mNextNode 成员不是 nullptr,那么这应该安排在线程池中运行(依此类推,直到 mNextNode 为 nullptr。通过这个 PriorityNodes 链表排序是我想要的线程池以通过其线程进行排序。PriorityNode 具有插入运算符,通常产生如下输出。(这意味着 1A1 可以与 1A2 并发运行,并且当这两个线程都完成时,下一个 PriorityNode 将允许 1B1、1B2、1B3 和1B4 并发运行 - 无论池中有多少线程可用。

1A1
1A2
+-1B1
+-1B2
+-1B3
+-1B4

我似乎最接近这个问题的解决方案 - 再次注意它是英特尔特定的,我在电源 PC 上是英特尔 TBB - here 是他们用于串行执行顺序的示例。

/**
 * 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;
};

【问题讨论】:

  • 虽然大多数线程池都是有效的任务队列,但我认为任务运行时没有任何内在的顺序保证。线程池通常是管理“后台和并行的独立工作项”的东西。因此,如果您有顺序和依赖性要求,那么线程池可能不是您想要的。我建议使用某种工作队列或(可能更好)演员框架。
  • @PeterRitchie 我编辑了问题以显示现场演示。那里的调度程序循环显示了一个潜在的占位符,可以“理论上”检查 PriorityNodes 以查看是否所有 PriorityNodes.mConcorrent 都已完成。不幸的是,我不知道该怎么做。我一直在研究 std::future 和 std::shared 期货来轮询线程完成状态 - 但它对于我的舒适度来说有点太高级了。
  • 您可以使用 shared_future 嵌套期货并让某些东西(包括未来)等待其他期货。但是,如果你想要更多独立的任务,我不确定未来是否可行。
  • @PeterRitchie 我在想类似嵌套期货的东西是可行的,我一直在寻找可以完成类似事情的示例代码,因为我对期货很陌生,尤其是共享期货。
  • 我不确定嵌套期货对性能的影响。例如,如果您将一个future 嵌套在另一个future 中,则第一个future 可能已经在线程上执行,生成另一个future(可能还有另一个线程)可能会引入可能会阻碍性能(或不是性能最高的代码)的上下文切换。例如:如果你已经在未来,需要做更多的工作,就在同一个未来做。这可能只是您调用的另一个 lambda。

标签: c++ c++11 threadpool tbb ppl


【解决方案1】:

为什么不在 PowerPC 上使用 TBB?它是一个高度可移植的库,旨在尽可能地跨平台;我听说它正在被 TBB 开源社区移植到 BlueGen 上。您可以在英特尔 TBB 论坛上询问他们,例如回复this forum thread

英特尔不为 TBB 分发 PowerPC 二进制文件,但您可以尝试从 sources 构建它,只需通过

制作tbb

另见these community patches

【讨论】:

  • 感谢您的提示,我错误地认为英特尔 TBB 不会编写适用于竞争架构的代码。既然这是一个选项,我会研究一下。
  • 我亲自参与了 PowerPC 移植工作,以使 TBB 能够在 IBM Blue Gene 系统上运行,并且已经工作了一段时间。我不记得具体是哪个版本第一次支持它,因为我们有一段时间的分叉,但 TBB 4.2 版本今天在 Blue Gene/Q 上运行。
【解决方案2】:

如果有人还在寻找这个,请在​​此处查看 repo - 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;
    });
}

它会按顺序执行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多