一 OpenMP 教程 深入人剖析 OpenMP reduction 子句( 二 )


#include <stdio.h>#include <omp.h>#include <unistd.h>static int data;int main() {#pragma omp parallel num_threads(2) reduction(+:data){for(int i = 0; i < 10000; i++) {data++;usleep(10);}printf("data = https://www.huyubaike.com/biancheng/%d tid = %d/n", data, omp_get_thread_num());}printf("In main function data = https://www.huyubaike.com/biancheng/%d/n", data);return 0;}在上面的程序当中我们使用了一个子句 reduction(+:data) 在每个线程里面对变量 data 进行拷贝,然后在线程当中使用这个拷贝的变量,这样的话就不存在数据竞争了,因为每个线程使用的 data 是不一样的,在 reduction 当中还有一个加号,这个加号表示如何进行规约操作,所谓规约操作简单说来就是多个数据逐步进行操作最终得到一个不能够在进行规约的数据 。
例如在上面的程序当中我们的规约操作是 + ,因此需要将线程 1 和线程 2 的数据进行 + 操作,即线程 1 的 data 加上 线程 2 的 data 值,然后将得到的结果赋值给全局变量 data,这样的话我们最终得到的结果就是正确的 。
如果有 4 个线程的话,那么就有 4 个线程本地的 data(每个线程一个 data) 。那么规约(reduction)操作的结果等于:
(((data1 + data2) + data3) + data4) 其中 datai 表示第 i 个线程的得到的 data。
除了后面的两种方法解决多个线程同时对一个数据进行操作的问题的之外我们还有一些其他的办法去解决这个问题,我们在下一篇文章当中进行仔细分析 。
深入剖析 reduction 子句我们在写多线程程序的时候可能会存在这种需求,每个线程都会得到一个数据的结果,然后在最后需要将每个线程得到的数据进行求和,相乘,或者逻辑操作等等,在这种情况下我们可以使用 reduction 子句进行操作 。redcution 子句的语法格式如下:
reduction(操作符:变量)当我们使用 reduction 子句的时候线程使用的是与外部变量同名的变量,那么这个同名的变量的初始值应该设置成什么呢?具体的设置规则如下所示:
运算符初始值+/加法0*/乘法1&&/逻辑与1||/逻辑或0min/最小值对应类型的最大值max/最大值对应类型的最小值&/按位与所有位都是 1|/按位或所有位都是 0^/按位异或所有位都是 0下面我们使用各种不同的例子去分析上面的所有的条目:
加法+操作符我们使用下面的程序去测试使用加法规约的正确性,并且在并行域当中打印进行并行域之前变量的值 。
#include <stdio.h>#include <omp.h>static int data;int main() {#pragma omp parallel num_threads(2) reduction(+:data){printf("初始值 : data = https://www.huyubaike.com/biancheng/%d tid = %d/n", data, omp_get_thread_num());if(omp_get_thread_num() == 0) {data = https://www.huyubaike.com/biancheng/10;}else if(omp_get_thread_num() == 1){data = 20;}printf("变化后的值 : data = https://www.huyubaike.com/biancheng/%d tid = %d/n", data, omp_get_thread_num());}printf("规约之后的值 : data = https://www.huyubaike.com/biancheng/%d/n", data);return 0;}上面的程序的输出结果如下所示:
初始值 : data = https://www.huyubaike.com/biancheng/0 tid = 0变化后的值 : data = 10 tid = 0初始值 : data = 0 tid = 1变化后的值 : data = 20 tid = 1规约之后的值 : data = 30从上面的输出结果我们可以知道当进入并行域之后我们的变量的初始值等于 0 ,第一个线程的线程 id 号等于 0 ,它将 data 的值赋值成 10 ,第二个线程的线程 id 号 等于 1,它将 data 的值赋值成 20。在出并行域之前会将两个线程得到的 data 值进行规约操作,在上面的代码当中也就是+操作,并且将这个值赋值给全局变量 data。
乘法*操作符#include <stdio.h>#include <omp.h>static int data = https://www.huyubaike.com/biancheng/2;int main() {#pragma omp parallel num_threads(2) reduction(*:data){printf("初始值 : data = https://www.huyubaike.com/biancheng/%d tid = %d/n", data, omp_get_thread_num());if(omp_get_thread_num() == 0) {data = https://www.huyubaike.com/biancheng/10;}else if(omp_get_thread_num() == 1){data = 20;}printf("变化后的值 : data = https://www.huyubaike.com/biancheng/%d tid = %d/n", data, omp_get_thread_num());}printf("规约之后的值 : data = https://www.huyubaike.com/biancheng/%d/n", data);return 0;}

经验总结扩展阅读