是否可以在工作后将线程连接到“并行”区域?

Posted

技术标签:

【中文标题】是否可以在工作后将线程连接到“并行”区域?【英文标题】:Is it possible to make thread join to 'parallel for' region after its job? 【发布时间】:2019-06-13 05:05:28 【问题描述】:

我有两个作业首先需要同时运行:

1) 可以并行化的for循环

2) 一个线程即可完成的功能

现在,让我描述一下我想做什么。

如果存在 8 个可用线程,

job(1) 和 job(2) 首先必须同时运行,分别使用 7 个线程和 1 个线程。

job(2) 完成后,job(2) 正在使用的线程应该分配给 job(1),它是并行 for 循环。

我使用omp_get_thread_num 来计算每个区域中有多少线程处于活动状态。我希望job(1) 中的线程数在job(2) 完成时增加1。

下面描述了一个可能错误或正确的解决方案:

  omp_set_nested(1);
  #pragma omp parallel
  
    #pragma omp sections
    
      #pragma omp section // job(2)
       // 'printf' is not real job. It is just used for simplicity.
        printf("i'm single: %d\n", omp_get_thread_num());
      
      #pragma omp section // job(1)
      
        #pragma omp parallel for schedule(dynamic, 32)
        for (int i = 0 ; i < 10000000; ++i) 
          // 'printf' is not real job. It is just used for simplicity.
          printf("%d\n", omp_get_thread_num());
        
      
    
  

如何才能完成我想要完成的工作?

【问题讨论】:

“我想在job(1)中使用这个线程(job(2))”是什么意思?在您的代码示例中,我没有看到在多个线程中使用任何结果。你只是打印到屏幕上。 你的意思是如果你有 8 个线程可用,7 个线程被分配给 job(1),1 个线程被分配给 job(2)。您希望在 job(2) 完成时为 job(1) 分配 8 个线程? @bradgonesurfing 'printf' 只是为了简单起见。那不是真正的工作。而且,你不明白的那句话是,如果用于'job(2)'的线程完成了它的工作,我希望这个线程在job(2)中并行使用for循环。 @bradgonesurfing 是的,你是对的。但是 job(1) 不能等待 'job(2)' 完成。 好的,那么您应该更新问题以澄清您的意思,因为它并不完全明显。 【参考方案1】:

这样的事情呢?

#pragma omp parallel

     // note the nowait here so that other threads jump directly to the for loop
    #pragma omp single nowait
    
       job2();
    

    #pragma omp for schedule(dynamic, 32)
    for (int i = 0 ; i < 10000000; ++i) 
        job1();
    

我没有对此进行测试,但是由于 nowait,单线程将仅由一个线程执行,而所有其他线程将直接跳转到 for 循环。 另外我认为它比章节更容易阅读。

【讨论】:

【参考方案2】:

另一种表达方式(可能是更好的方式)是使用 OpenMP 任务:

#pragma omp parallel master

    #pragma omp task // job(2)
     // 'printf' is not real job. It is just used for simplicity.
        printf("i'm single: %d\n", omp_get_thread_num());
    
    #pragma omp taskloop // job(1)
    for (int i = 0 ; i < 10000000; ++i) 
        // 'printf' is not real job. It is just used for simplicity.
        printf("%d\n", omp_get_thread_num());
    

如果你的编译器不理解 OpenMP 5.0 版本,那么你必须拆分parallelmaster

#pragma omp parallel
#pragma omp master

    #pragma omp task // job(2)
     // 'printf' is not real job. It is just used for simplicity.
        printf("i'm single: %d\n", omp_get_thread_num());
    
    #pragma omp taskloop ]
    for (int i = 0 ; i < 10000000; ++i) 
        // 'printf' is not real job. It is just used for simplicity.
        printf("%d\n", omp_get_thread_num());
    

【讨论】:

我很好奇:您建议master 有什么特别的原因吗?我见过的所有材料都使用single——尽管也没有特别的原因。我认为在这种情况下它应该无关紧要,但如果可能的话,人们可能会主张通常更喜欢single 共有三种模式:mastersinglesections。我使用master 作为OpenMP 5.0 引入parallel master 作为为此目的的组合结构。 parallel single 版本(尚)不存在。也可以使用parallel sections 并且只有一个部分,可以避免使用section 指令。 请注意 omp taskloop 需要 openmp 4.5 或更高版本。 omp 任务可能会导致小型作业的开销,除非作业与任务开销相比非常耗时,否则我会避免它 @amlucas 你对任务开销和动态调度开销有什么想法吗?【参考方案3】:

问题来自同步。在section 结束时,omp 等待所有线程的终止,并且在检查其完成之前不能释放作业 2 上的线程。

解决方案需要抑制与nowait 的同步。 我没有成功抑制与sections 和嵌套并行性的同步。我很少使用嵌套并行区域,但我认为,虽然可以等待部分,但在一个部分内生成新的嵌套并行区域时会出现问题。在无法抑制的并行部分末尾有一个强制同步,它可能会阻止新线程加入池。

我所做的是使用single 线程,没有同步。这样,omp 启动 single 线程并且不等待其完成来启动并行 for。当线程完成其single 工作时,它会加入线程池以完成对for 的处理。

#include <omp.h>
#include <stdio.h>

int main() 
  int singlethreadid=-1;
  // omp_set_nested(1);
#pragma omp parallel
  
#pragma omp single nowait  // job(2)
     // 'printf' is not real job. It is just used for simplicity.
      printf("i'm single: %d\n", omp_get_thread_num());
      singlethreadid=omp_get_thread_num();
    
#pragma omp for schedule(dynamic, 32) 
    for (int i = 0 ; i < 100000; ++i) 
      // 'printf' is not real job. It is just used for simplicity.
      printf("%d\n", omp_get_thread_num());
      if (omp_get_thread_num() == singlethreadid)
        printf("Hello, I\'m back\n");
    
  

【讨论】:

虽然解决方案很好,但为什么建议的section-approach 不起作用的解释是错误的。 section 的末尾没有同步!问题是只有一个线程甚至进入了section - 并且嵌套的parallel 区域与外部并行区域的其他线程完全没有关系。 我打错了。我的意思是 sectionS 并且在 sectionS 区域的末尾有一个同步。一个部分是单线程的,同步没有意义。 是的,但在原始示例中只有一个 sections 构造。这不是sections 末尾的同步阻止线程在第二部分提供帮助。事实上,每个section只有一个线程执行,以及嵌套并行的工作原理。

以上是关于是否可以在工作后将线程连接到“并行”区域?的主要内容,如果未能解决你的问题,请参考以下文章

多个并行tcp连接

为啥我的手机显示无法连接到服务器

问:如何在没有 USB 或连接到计算机的设备上运行 Flutter 应用程序

LibreOffice BASIC:连接到PostgreSQL

Greenplum 并行下载转储到本地集群

多端口监听套接字linux