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等等。