如何在工作线程中重用主线程创建的OMP线程池?

Posted

技术标签:

【中文标题】如何在工作线程中重用主线程创建的OMP线程池?【英文标题】:How to Reuse OMP Thread Pool, Created by Main Thread, in Worker Thread? 【发布时间】:2016-07-07 20:39:34 【问题描述】:

在我的 c++ 应用程序开始时,我的主线程使用 OMP 并行化多个 for 循环。在第一个并行化 for 循环之后,我看到使用的线程在应用程序期间仍然存在,并被重用于后续 OMP 循环,使用命令(在 CentOS 7 中工作)从主线程执行:

for i in $(pgrep myApplication); do ps -mo pid,tid,fname,user,psr -p $i;done

稍后在我的程序中,我从主线程启动了一个 boost 线程,其中我使用 OMP 并行化了一个 for 循环。此时,我看到创建了一组全新的线程,这具有相当大的开销。

是否可以让boost线程内的OMP并行for循环复用主线程创建的原始OMP线程池?

编辑:一些伪代码:

myFun(data)


    // Want to reuse OMP thread pool from main here.
    omp parallel for
    for(int i = 0; i < N; ++i)
    
       // Work on data
    




main


    // Thread pool created here.
    omp parallel for
    for(int i = 0; i < N; ++i)
    
        // do stuff
    


    boost::thread myThread(myFun) // Constructor starts thread.

    // Do some serial stuff, no OMP.

    myThread.join();



【问题讨论】:

OpenMP 与其他线程机制的交互不是规范的一部分,因此在很大程度上取决于实现。 GNU OpenMP 运行时在 TLS 中保留一个指向线程池的指针,并将其传播到(嵌套的)团队。通过pthread_create(或boost::threadstd::thread)启动的线程不会继承指针,因此会生成一个新池。其他 OpenMP 运行时可能也是如此。 @HristoIliev 谢谢,这也是我从阅读中收集到的。您是否知道任何解决方法?我可以修改我的应用程序以保持 boost::thread 实例处于活动状态以重用其 OMP 线程池,但我希望能快速修复。 我不知道有任何解决方法。此外,从最终用户的角度来看,OpenMP 运行时应该是一个黑匣子,以使范例保持可移植性。 @HristoIliev 这很公平。我不明白他们为什么不让 OMP 线程池成为全球性的,但我相信他们有他们的理由。感谢您确认没有解决方法或我错过了什么。随意将您的 cmets 复制粘贴到回复中,我会接受它们作为答案。 我添加了一个答案,希望能回答您的问题。 【参考方案1】:

OpenMP 与其他线程机制的交互被故意排除在规范之外,因此严重依赖于实现。 GNU OpenMP 运行时在 TLS 中保留一个指向线程池的指针,并将其传播到(嵌套的)团队。通过pthread_create(或boost::threadstd::thread)启动的线程不会继承指针,因此会生成一个新池。其他 OpenMP 运行时可能也是如此。

标准中有一个要求基本上强制大多数实现中的这种行为。它是关于 threadprivate 变量的语义以及它们的值如何在从同一线程分叉的不同并行区域中保留(OpenMP 标准,2.15.2 threadprivate Directive):

仅当满足以下所有条件时,才能保证非初始线程的线程私有变量中的数据值在两个连续的活动parallel 区域之间持续存在:

parallel 区域均未嵌套在另一个显式并行区域内。 用于执行两个parallel 区域的线程数相同。 用于执行两个parallel 区域的线程关联策略相同。 封闭任务区域中的 dyn-var 内部控制变量的值在进入两个 parallel 区域时为 false

如果这些条件都成立,并且两个区域都引用了一个线程私有变量,那么在各自区域中具有相同线程号的线程将引用该变量的相同副本。

除了性能之外,这可能是在 OpenMP 运行时中使用线程池的主要原因。

现在,假设由两个独立线程分叉的两个并行区域共享同一个工作线程池。第一个线程分叉了一个并行区域,并设置了一些线程私有变量。稍后,第二个并行区域由同一个线程分叉,其中使用了这些线程私有变量。但是在两个并行区域之间的某个地方,一个并行区域由第二个线程分叉,并且使用来自同一池的工作线程。由于大多数实现在 TLS 中保留线程私有变量,因此无法再断言上述语义。一种可能的解决方案是为每个单独的线程向池中添加新的工作线程,这与创建新线程池没有太大区别。

我不知道任何使工作线程池共享的解决方法。如果可能的话,它不会是可移植的,因此 OpenMP 的主要优点将丢失。

【讨论】:

以上是关于如何在工作线程中重用主线程创建的OMP线程池?的主要内容,如果未能解决你的问题,请参考以下文章

线程池三问—百度真题

线程NO.9 线程池

线程池

c# 怎么等待线程池中所有线程都运行结束在运行主线程

线程池

在 C 中的循环中重用条件变量