首页 > 解决方案 > OpenMP - 使用 nowait 运行单个区域,然后在 for 循环中加入其他线程

问题描述

我想做的是函数的第一部分在一个线程中运行,其他线程启动第二个函数(这个函数有三个循环),在单线程结束第一个函数后,他与其他人一起帮助循环。

我编写了以下代码,这是错误的,因为所有线程都运行第二个函数,并且只需要一个即可运行,但所有线程都有助于循环。

void main(){


/* code */


#pragma omp parallel
{
     #pragma omp single nowait
     {
      std::cout << "Running func 1:" << omp_get_thread_num() << std::endl;
      func1();
      std::cout << "Finish func 1" <<std::endl;
     }

#pragma omp nowait

std::cout << "Running func 2:" << omp_get_thread_num() << std::endl;
func2();

#pragma omp barrier
}//close parallel


/* more code */

}//close main



void func2(void){

        /* code to read file */ 



#pragma omp parallel
{


     for (int i=40;i<60;i++){
          #pragma omp for nowait
          for (int j=0;j<100;j++){
           /* code  */
          }
      }

#pragma omp for schedule(dynamic,1) nowait
  for (int i=0;i<40;i++){
        for (int j=0;j<100;j++){
         /* code  */
       }
  }

#pragma omp for schedule(dynamic)
  for (int i=60;i<100;i++){
        for (int j=0;j<100;j++){
         /* code  */
        }
  }

         /* code to write file */ 

}//close parallel

#pragma omp barrier
} //close func2

我的终端显示:

Running func 1: 0
Running func 2: 1
Running func 2: 2
Running func 2: 3 
Finish func 1
Running func 2: 0 

编辑

Obs.: Func1 应该只在一个线程上执行

func2 被分成三个 for 循环,因为第一个 for 比其他所有循环消耗更多的时间。如果我只使用一个 for 循环,所有其他线程将结束,一些线程将继续运行。这样,首先计算困难。

使用 Jim Cownie 建议的代码,func1 和 func2 并行运行,但是要么线程运行 func2 两次,要么只有一个线程单独运行而无需其他线程的帮助。

即使有必要使用任务或部分,我有什么想法吗?

标签: c++multithreadingopenmpthread-synchronization

解决方案


您的代码有很多问题

  1. 没有这样的 openMP 指令#pragma omp nowait,因此您甚至可能没有在启用 OpenMP 的情况下进行编译(因为,当它启用时,您应该收到一条错误消息;例如,请参阅https://godbolt.org/z/EbYV6h
  2. #pragma omp barrier在并行区域结束之前永远不需要 a (因为将执行下一个串行区域的主线程不能离开,直到所有线程也完成在并行区域中的执行。)

我不明白你为什么要使用嵌套并行。您已经在并行执行 func2() ,因此此处的任何嵌套都会导致超额订阅。

你可以像这样实现你想要的

#pragma omp parallel
{
#pragma omp single nowait
    func1()
  func2();
}

void func2()
{
#pragma omp for schedule(dynamic), nowait
    for (...)
        ... etc ...
}

或者,通过使用任务和任务循环,这可能是一种更简洁的表达方式。

使用任务,(并且,在您澄清您只想function2执行一次之后(我正在阅读代码所说的,因为这比读心术更容易!)),这样的事情有效

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

void function1()
{
  fprintf(stderr,"%d: entering function1\n", omp_get_thread_num());
  sleep(1);
  fprintf(stderr,"%d: leaving function1\n", omp_get_thread_num());
}

void function2()
{
  fprintf(stderr,"%d: entering function2\n", omp_get_thread_num());
#pragma omp taskloop grainsize(1)                                                                                   
  for (int i=0; i<10; i++)
    {
      fprintf(stderr,"%d: starting iteration %d\n",
                     omp_get_thread_num(),i);
      sleep(1);
      fprintf(stderr,"%d: finishing iteration %d\n",
                     omp_get_thread_num(),i);
    }
  fprintf(stderr,"%d: leaving function2\n", omp_get_thread_num());
}

int main()
{
#pragma omp parallel
  {
#pragma omp single
    {
      fprintf(stderr,"Executing with %d threads\n",
                      omp_get_num_threads());
#pragma omp task
      {
        function1();
      }
#pragma omp task
      {
        function2();
      }
    }
  }
}

这是在四个线程上执行,当然其他交错也是可能的。

OMP_NUM_THREADS=4 ./a.out
Executing with 4 threads
3: entering function2
2: entering function1
0: starting iteration 0
1: starting iteration 1
3: starting iteration 9
1: finishing iteration 1
3: finishing iteration 9
0: finishing iteration 0
3: starting iteration 8
1: starting iteration 2
2: leaving function1
0: starting iteration 3
2: starting iteration 4
3: finishing iteration 8
1: finishing iteration 2
3: starting iteration 7
0: finishing iteration 3
0: starting iteration 6
2: finishing iteration 4
1: starting iteration 5
0: finishing iteration 6
3: finishing iteration 7
1: finishing iteration 5
3: leaving function2

您可以看到只有一个线程执行每个函数[12],并且循环迭代在所有线程中共享。


推荐阅读