您的位置:首页 > 运维架构

OpenMP并行程序设计之常用子句使用介绍(二)

2017-02-26 20:57 477 查看
OpenMP并行程序设计之常用子句使用介绍(二)
    承接上面的博客,这次还要介绍三个OpenMP并行程序设计的数据处理子句,threadprivate子句,reduction子句以及sections子句。

1.  threadprivate子句

    这个子句理解较为简单,作用呢,就是对于一些全局变量,可以使用这条指令,使得每一个线程都能有此全局变量的独立的拷贝,并且互相不影响。需要注意的是,该指令所指示的一定是全局变量,即在全程序是有效的,而且是永久变量,在多个并行域内都有效。我们代码走起。
#include<iostream>
#include <omp.h>
int g;//全局变量

#pragma omp threadprivate(g)  //声明变量线程私有
int main(int argc, char *argv[])
{
/* Explicitly turn off dynamic threads */
//omp_set_dynamic(0);

printf("Masterthread started\n\n");

#pragma omp parallel
{
g = omp_get_thread_num();
printf("tid: %d\n", g);
} // End of parallel region

#pragma omp parallel
{
int temp = g*g;
printf("tid : %d, tid*tid: %d\n", g, temp);
} // End of parallel region

printf("Masterthread finished\n");
system("pause");
return(0);

}
程序运行结果如下:



    从上面的程序以及运行结果可以看出,有两个并行域,两个并行域共享全局变量g,而且在各自并行域中各个线程共享变量g,但是在各个线程中g值的输出是互不影响的,是相互独立的。

2.   reduction子句

    在介绍这个子句之前,请先看下面示例

#include <iostream>
void test()
{
int sum = 0;
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
#pragma omp parallel for
for (int i = 0; i < 10; i++)
sum = sum + a[i];
std::cout << "sum: " << sum << std::endl;
}
int main()
{
for (int i = 0; i < 10; i++)
test();
system("pause");
return 0;
}
程序运行结果如下:



    诶,为什么会出现这样的运行结果呢,按理来说,每次执行test()函数,得到的sum的求和值应该都是55啊,为啥还会出现27,47,40,42呢。之所以会出现上述错误的输出结果就是因为存在数据竞争的问题。这应该是所有多线程编程中最为棘手的问题了,那么对于这样的并行处理求和该怎么办呢,reduction子句就能派上用场。我们对上面的代码做一些小的修改,我们在#pragma
omp parallel for 后面加上了 reduction(+:sum)。代码如下:

#include <iostream>
void test()
{
int sum = 0;
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
#pragma omp parallel for  reduction(+:sum)
for (int i = 0; i < 10; i++)
sum = sum + a[i];
std::cout << "sum: " << sum << std::endl;
}
int main()
{
for (int i = 0; i < 10; i++)
test();
system("pause");
return 0;
}
程序运行结果如下:



   从上面的运行结果可以看出,所有结果都是正确的。reduction(+:sum)它的意思是告诉编译器:下面的for循环你要分成多个线程跑,但每个线程都要保存变量sum的拷贝,循环结束后,所有线程把自己的sum累加起来作为最后的输出。

3.   section子句和sections子句

    section语句是用在sections语句里用来将sections语句里的代码划分成几个不同的段,每段都并行执行。用法如下:

#pragma omp [parallel] sections [子句]
{
#pragma omp section
{
代码块
}
}

下面我们来看个实例吧,代码最具有说服力,最能让人理解。

#include <iostream>
#include<omp.h>
int main(int argc, char *argv)
{
#pragma omp parallel sections
{
#pragma omp section
printf("section 1 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
printf("section 2 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
printf("section 3 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
printf("section 4 ThreadId = %d\n", omp_get_thread_num());
}
system("pause");
return 0;
}

程序运行结果如下:



    从结果中可以发现第3段代码执行比第2段代码早,说明各个section里的代码都是并行执行的,并且各个section被分配到不同的线程执行。使用section语句时,需要注意的是这种方式需要保证各个section里的代码执行时间相差不大,否则某个section执行时间比其他section过长就达不到并行执行的效果了

    上面的代码也可以改写成以下形式:

#include <iostream>
#include<omp.h>
int  main(int argc, char *argv)
{
#pragma omp parallel
{
#pragma omp sections//sections 1
{
#pragma omp section
printf("section 1 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
printf("section 2 ThreadId = %d\n", omp_get_thread_num());
}
#pragma omp sections
{

#pragma omp section//sections 2
printf("section 3 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
printf("section 4 ThreadId = %d\n", omp_get_thread_num());
}
}
system("pause");
return 0;
}


程序运行结果:



   这种方式和前面那种方式的区别是,两个sections语句是串行执行的,即第二个sections语句里的代码要等第一个sections语句里的代码执行完后才能执行。个人认为OpenMP并行程序设计比较常用的数据子句也就这些,当然还有其他子句了,比如copyin,default等等。

参考文献: http://blog.csdn.net/gengshenghong/article/details/6969957 http://blog.csdn.net/wangzhebupt/article/details/22743515
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: