Using OpenMP (二) 共享变量和私有变量
2017/10/22
by
CHENJING DING
CHAPTER 2 – 共享变量和私有变量
共享变量和私有变量
共享变量
所有线程都可以读写的变量
私有变量
每一个线程都有自己的备份的变量,其他线程不可以读写该变量(除非将该变量的指针给其他线程)
区分
在#pragma omp parallel 内部定义的为私有变量,反之为共享变量;
default, private, shared, firstprivate可以改变变量的属性,忽略其定义的位置;
shared
语法:shared(item-list); //eg:shared(a,b)
a,b变量是共享的,其他进程都可读写
private
语法:private(item-list)
每个线程会拷贝该变量,对其他进程不可见;且该变量在进入并行区域不会被初始化;下面代码中,由于A在并行区域内还没有定义,所以会出现错误
int main(int argc, _TCHAR* argv[])
{
int A=100;
#pragma omp parallel for private(A)
for(int i = 0; i<10;i++)
{
printf("%d\n",A);
}
return 0;
}
并行区域内的private变量和并行区域外同名的变量没有存储关联,如下。
int main(int argc, _TCHAR* argv[])
{
int C = 100;
#pragma omp parallel for private(C)
for(int i = 0; i<10;i++)
{
C = 200;
printf("%d\n",C);
}
printf("%d\n",C);
return 0;
}
最后并行区域外输出的C为100;
firstprivate
继承并行区域之外的变量的值,用于在进入并行区域之前进行一次初始化。每一个线程都会初始化
int main(int argc, _TCHAR* argv[])
{
int A=100;
#pragma omp parallel for firstprivate(A)
for(int i = 0; i<10;i++)
{
printf("%d\n",A); //A被初始化为100
}
return 0;
}
lastprivate
退出并行区域时,需要将其值赋给同名的共享变量,OpenMP规范中指出,如果是循环迭代,那么是将最后一次循环迭代中的值赋给对应的共享变量;
#include <omp.h>
int main(int argc, _TCHAR* argv[])
{
int A = 100;
#pragma omp parallel for lastprivate(A)
for(int i = 0; i<10;i++)
{
printf("Thread ID: %d, %d\n",omp_get_thread_num(), i); // #1
A = i;
}
printf("%d\n",A); // #2 此时A输出为9;
return 0;
}
不能对一个变量同时使用两次private,或者同时使用private firstprivate/lastprivate,只能firstprivate和lastprivate一起使用。
Default
default(shared):表示并行区域内的共享变量在不指定的情况下都是shared属性
default(none):表示必须显式指定所有共享变量的数据属性,否则会报错,除非变量有明确的属性定义(比如循环并行区域的循环迭代变量只能是私有的)
#define COUNT 10
int main(int argc, _TCHAR* argv[])
{
int sum = 0;
int i = 0;
#pragma omp parallel for default(none)
for(i = 0; i < COUNT;i++)
{
sum = sum + i;
}
printf("%d\n",i);
printf("%d\n",sum);
return 0;
}
使用default(none),那么编译会报错“没有给sum指定数据共享属性”,不会为变量i报错,因为i是有明确的含义的,只能为私有。
练习1
利用下图求和公式计算pi的值,利用多线程完成计算过程,其中每个线程内部的求和localsum是私有变量,而最后结果sum需要读所有局部和加,因此结果sum应为共享变量;
// helloomp.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "omp.h"
#include"conio.h"
#include
#include
#define N 200
#define num_of_threads 4
int main()
{
//全局变量在多线程里面是共享的
double delta = 1.0/N;
double localsum = 0;
double sum = 0;
double xi = 0;
//多线程开始
#pragma omp parallel num_threads(num_of_threads) firstprivate(xi,localsum)
{
//xi为私有,且初始化为0
int id = omp_get_thread_num();;
printf("This %d thread \n", id);
//每个线程的私有变量
for (int i = id; i < N; i = i + num_of_threads) {
xi = i *delta;
localsum = localsum + 4.0 / ((1 + xi*xi))*delta;
}
#pragma omp atomic //atomic表示每一个线程在写入共享变量sum时,其他线程不能读写sum
sum += localsum;
}
//多线程结束
printf("%f",sum);
int A = 100;
getchar();
return 0;
}
使用Reduction实现
语法:reduction (operator:list)
使用reduction可以避免数据竞争的发生,reduction子句为变量指定一个操作符(+, *, -, &, |,ˆ, &&, ||, max,min),每个线程都会创建reduction变量的私有拷贝,在OpenMP区域结束处,将使用各个线程的私有拷贝的值通过指定的操作符进行迭代运算,并赋值给原来的变量。
// helloomp.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "omp.h"
#include"conio.h"
#include
#include
#define N 200
#define num_of_threads 4
int main()
{
//全局变量在多线程里面是共享的
double delta = 1.0/N;
double sum = 0;
//多线程开始
#pragma omp parallel num_threads(num_of_threads) reduction(+:sum)
{
double xi = 0;
int id = omp_get_thread_num();;
printf("This %d thread \n", id);
//每个线程的私有变量
for (int i = id; i < N; i = i + num_of_threads) {
xi = i *delta;
sum = sum + 4.0 / ((1 + xi*xi))*delta;
//每一个线程有sum的拷贝,在线程结束时,会将所有线程的sum加起赋给共享变量sum
}
}
//多线程结束
printf("%f",sum);
int A = 100;
getchar();
return 0;
}
练习2
利用id进程号实现for循环(SPMD方法)
两个线程分别执行for循环的i=[0,1,2,3,4]和i=[5,6,7,8,9]
// helloomp.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "omp.h"
#include"conio.h"
#include
#include
#define N 10 //数组个数
#define num_of_threads 2 //线程个数
int main()
{
int i;
double x[N], y[N], z[N], alpha = 5.0;
for (i = 0; i < N; i++) {
x[i] = i;
y[i] = 2.0*i;
}
int num = 4;
//并行部分
#pragma omp parallel num_threads(num_of_threads)
{
int id = omp_get_thread_num();
int chunk = N / num_of_threads;
int start = id*chunk;
int end = (id+1)*chunk ;
if (id == num_of_threads-1 ) end = N;
//进程号从0开始,如果N/num_of_threads除不尽,就让最后一个线程执行完for循环
printf("This %d thread \n", id);
for (i = start; i < end; i++)
{
z[i] = alpha *x[i] + y[i];
}
}
printf("[");
for (i = 0; i < N; i++)
{
printf("%.1f ",z[i] );
}
printf("]\n");
getchar();
return 0;
}