using OMP(二) 共享变量和私有变量

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;
}


    
    
   
   

 

 

 

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值