可以参考 http://blog.csdn.net/gengshenghong/article/details/6970220 可以查看哪些指令能接受哪些子句。
(1) private
private子句将一个或多个变量声明为线程的私有变量。每个线程都有它自己的变量私有副本,其他线程无法访问。即使在并行区域外有同名的共享变量,共享变量在并行区域内不起任何作用,并且并行区域内不会操作到外面的共享变量。
注意:
1. private variables are undefined on entry and exit of the parallel region.即private变量在进入和退出并行区域是“未定义“的。
2. The value of the original variable (before the parallel region) is undefined after the parallel region!在并行区域之前定义的原来的变量,在并行区域后也是”未定义“的。
3. A private variable within the parallel region has no storage association with the same variable outside of the region. 并行区域内的private变量和并行区域外同名的变量没有存储关联。
说明:private的很容易理解错误。下面用例子来说明上面的注意事项,
A. private变量在进入和退出并行区域是”未定义“的。
其实,在VS中编译这段代码,就会有编译警告:
warning C4700: uninitialized local variable 'A' used
很清楚的指向"printf"这句,A是没有初始化的变量。所以,运行时候会出现运行时崩溃的错误。
这段代码能说明,private在进入并行区域是未定义的,至于退出并行区域就不容易举例说明了,本身,这里的三个注意事项是交叉理解的,说明的是一个含义,所以,看下面的例子来理解。
B. 在并行区域之前定义的原来的变量,在并行区域后也是”未定义“的。
C. 并行区域内的private变量和并行区域外同名的变量没有存储关联
总结来说,上面的三点是交叉的,第三点包含了所有的情况。所以,private的关键理解是:A private variable within the parallel region has no storage association with the same variable outside of the region.简单点理解,可以认为,并行区域内的private变量和并行区域外的变量没有任何关联。如果非要说点关联就是,在使用private的时候,在之前要先定义一下这个变量,但是,到了并行区域后,并行区域的每个线程会产生此变量的副本,而且是没有初始化的。
下面是综合上面的例子,参考注释的解释:
(2)firstprivate
Private子句的私有变量不能继承同名变量的值,firstprivate则用于实现这一功能-继承并行区域额之外的变量的值,用于在进入并行区域之前进行一次初始化。
Firstprivate(list):All variables in the list areinitialized with the value the original object had before entering the parallelconstruct.
分析下面的例子:
再看下面的例子,和前面private的例子很类似:
继续探讨这里的“进行一次初始化”,为了理解“一次”的含义,看下面的例子:
其实,这个结果是很容易理解的,不可能是每一个for都有一个变量的副本,而是每一个线程,所以这个结果在预料之中。
仍然借助上面这个例子,帮助理解private和firstprivate,从而引出lastprivate,private对于并行区域的每一个线程都有一个副本,并且和并行区域外的变量没有关联;firstprivate解决了进入并行区的问题,即在进入并行区域的每个线程的副本变量使用并行区域外的共享变量进行一个初始化的工作,那么下面有一个问题就是,如果希望并行区域的副本变量,在退出并行区的时候,能反过来赋值给并行区域外的共享变量,那么就需要依靠lastprivate了。
(3)lastprivate
如果需要在并行区域内的私有变量经过计算后,在退出并行区域时,需要将其值赋给同名的共享变量,就可以使用lastprivate完成。
Lastprivate(list):The thread that executes the sequentially last iteration or section updates thevalue of the objects in the list.
从上面的firstprivate的最后一个例子可以看出,并行区域对A进行了赋值,但是退出并行区域后,其值仍然为原来的值。
这里首先有一个问题是:退出并行区域后,需要将并行区域内的副本的值赋值为同名的共享变量,那么,并行区域内有多个线程,是哪一个线程的副本用于赋值呢?
是否是最后一个运行完毕的线程?否!OpenMP规范中指出,如果是循环迭代,那么是将最后一次循环迭代中的值赋给对应的共享变量;如果是section构造,那么是最后一个section语句中的值赋给对应的共享变量。注意这里说的最后一个section是指程序语法上的最后一个,而不是实际运行时的最后一个运行完的。
在理解这句话之前,先利用一个简单的例子来理解一下lastprivate的作用:
理解了lastprivate的基本含义,就可以继续来理解上面的红色文字部分的描述了,即到底是哪一个线程的副本用于对并行区域外的变量赋值的问题,下面的例子和前面firstprivate的例子很类似:
另外,firstprivate和lastprivate分别是利用共享变量对线程副本初始化(进入)以及利用线程副本对共享变量赋值(退出),private是线程副本和共享变量无任何关联,那么如果希望进入的时候初始化并且退出的时候赋值呢?事实上,可以对同一个变量使用firstprivate和lastprivate的,下面的例子即可看出:
关于lastprivate,还需要说明的一点是,如果是类(class)类型的变量使用在lastprivate参数中,那么使用时有些限制,需要一个可访问的,明确的缺省构造函数,除非变量也被使用作为firstprivate子句的参数;还需要一个拷贝赋值操作符,并且这个拷贝赋值操作符对于不同对象的操作顺序是未指定的,依赖于编译器的定义。
另外,firstprivate和private可以用于所有的并行构造块,但是lastprivate只能用于for和section组成的并行块之中,参考http://blog.csdn.net/gengshenghong/article/details/6970220的对照表。
(4)threadprivate
首先,threadprivate和上面几个子句的区别在于,threadprivate是指令,不是子句。threadprivate指定全局变量被OpenMP所有的线程各自产生一个私有的拷贝,即各个线程都有自己私有的全局变量。一个很明显的区别在于,threadprivate并不是针对某一个并行区域,而是整个于整个程序,所以,其拷贝的副本变量也是全局的,即在不同的并行区域之间的同一个线程也是共享的。
threadprivate只能用于全局变量或静态变量,这是很容易理解的,根据其功能。
根据下面的例子,来进一步理解threadprivate的使用:
分析结果,发现,第二个并行区域是在第一个并行区域的基础上继续递增的;每一个线程都有自己的全局私有变量。另外,观察在并行区域外的打印的“Globa A”的值可以看出,这个值总是前面的thread 0的结果,这也是预料之中的,因为退出并行区域后,只有master线程运行。
threadprivate指令也有自己的一些子句,就不在此分析了。另外,如果使用的是C++的类,对于类的构造函数也会有类似于lastprivate的一些限制。
总结:
private/firstprivate/lastprivate都是子句,用于表示并行区域内的变量的数据范围属性。其中,private表示并行区域team内的每一个线程都会产生一个并行区域外同名变量的共享变量,且和共享变量没有任何关联;firstprivaet在private的基础上,在进入并行区域时(或说每个线程创建时,或副本变量构造时),会使用并行区域外的共享变量进行一次初始化工作;lastprivate在private的基础上,在退出并行区域时,会使用并行区域内的副本的变量,对共享变量进行赋值,由于有多个副本,OpenMP规定了如何确定使用哪个副本进行赋值。另外,private不能和firstprivate/lastprivate混用于同一个变量,firstprivate和lastprivate可以对同一变量使用,效果为两者的结合。
threadprivate是指令,和private的区别在于,private是针对并行区域内的变量的,而threadprivate是针对全局的变量的。