炼数成金 门户 CUDA 查看内容

OpenMP多线程应用程序编程技术之并行区域编程

2015-9-11 13:48| 发布者: 炼数成金_小数| 查看: 1211| 评论: 0|原作者: beck_zhou|来自: CSDN

摘要: 其中block是需要在多个线程中执行的代码块,每一个线程在遇到并行区域的编译指导语句时,都会同时执行跟随其后的程序代码块。在编译指导语句后面也可以跟随一些子句,包括private,reduction等子句都可以在并行区域 ...

编程 C++ 编程技术

2.3.2并行区域编程
并行区域简单地说就是通过循环并行化编译指导语句使一段代码能够在多个线程内部同时执行。在C/C++语言中,并行区域编写的格式为:
    #pragma omp parallel [clause[clause]…]
             block
其中block是需要在多个线程中执行的代码块,每一个线程在遇到并行区域的编译指导语句时,都会同时执行跟随其后的程序代码块。在编译指导语句后面也可以跟随一些子句,包括private,reduction等子句都可以在并行区域指导语句中出现。parallel与parallel for语句类似,在使用时也有一定的限制。程序块必须是单一入口和单一出口的,不能从外面转入到程序块内部,也不能从程序块内部有多个出口转到程序块之外。下面用两个示例程序来说明编译指导语句parallel的使用,例3_1使用的是并行区域编译指导语句,例3_2使用的循环并行化的编译指导语句。

例2_1 使用并行区域编译指导语句
    #pragma omp parallel            // 并行执行
              for(int i=0; i<2; i++)
                  printf("Hello world i=%d/n", i);
输出结果为:
Hello world i=0
Hello world i=1
Hello world i=0
Hello world i=1
例2_2 使用循环并行化的编译指导语句
    #pragma omp parallel for         // 并行执行
              for(int i=0; i<2; i++)
                  printf("Hello world i=%d/n", i);
输出结果为:
Hello world i=0
Hello world i=1
两个程序的区别是使用的编译指导语句不同,一个使用的是parallel另一个使用的是parallel for。从结果可以看出并行区域与循环并行化的区别,即并行区域采用复制执行方式,代码在所有线程都执行一遍(环境变量OMP_NUM_THREADS=2);而循环并行化则采用了工作分配的执行方式,将循环的所有工作分配到各个线程中执行,所有线程工作的总和等于原来串行时的工作量。

小结:在程序遇到编译指导语句#pragma omp parallel时,会根据环境变量OMP_NUM_THREADS的值生成相应数量的线程,将代码复制到各个线程中执行。

上述parallel编译指导语句提供了一种简单的并行方法,能够将工作在多个线程的代码重复运行。但是这并不能提高程序的效率,我们希望的是工作被分配到多个线程中,由各个线程合作完成。在OpenMP中,每一个线程都可以调用omp_get_thread_num()函数来获得自己的线程号,并可以利用这个线程号来获得不同的工作任务。在OpenMP语法中可以通过编译指导语句#pragma omp parallel for进行循环并行化达到并行的目的,也可以用sections编译指导语句和section子句将不同的任务编写成不同的代码片段并行执行,下面将举例说明。
 
1.  工作分区编码
例2_3
    #pragma omp parallel sections
       {  #pragma omp section
                printf("section 1 thread=%d/n", omp_get_thread_num());
          #pragma omp section
                printf("section 2 thread=%d/n", omp_get_thread_num());
       #pragma omp section
                printf("section 3 thread=%d/n", omp_get_thread_num());
        }
输出结果为:
section 1 thread=0
section 2 thread=1
section 3 thread=0
可以看到,在使用工作分区编码的时候,各个线程自动从各个分区中获得任务执行。并且在执行完一个分区的时候,如果分区里还有未完成的工作,则继续取得任务执行。
 
2.  线程私有数据与threadprivate子句
除了private子句能够产生线程私有的变量之外,还需要考虑一些全局的数据。这些全局的数据可能是整个程序运行过程中都需要的数据,或是在源程序中跨多个文件所需要的变量。在通常情况下,这些数据都是共享的数据,所有线程访问的都是共享内存空间中的同一内存地址内容。然而,有些时候,对于每一个线程来说,可能需要生成自己私有的线程数据,此时,就需要使用threadprivate子句来标明某一个变量是线程私有数据,在程序运行的过程中,不能够被其它线程访问到。
例2_4
int counter=0;
#pragma omp threadprivate(counter)    //使用threadprivate子句
void inc_counter()
{counter++;}
int _tmain(int argc, _TCHAR* argv[]){ 
#pragma omp parallel
         for(int i=0;i<10000;i++)
               inc_counter();
         printf("counter=%d/n",counter);
}
输出结果为:
counter=10000
程序中使用了threadprivate子句,将counter变量变成一个私有变量。若将#pragma omp threadprivate(counter)语句去掉,则全局变量counter变为共享,此时下面区域并行部分会产生数据冲突,执行结果将不可知。

鲜花

握手

雷人

路过

鸡蛋

最新评论

热门频道

  • 大数据
  • 商业智能
  • 量化投资
  • 科学探索
  • 创业

即将开课

热门文章

     

    GMT+8, 2020-1-21 20:38 , Processed in 0.106758 second(s), 23 queries .