多線程環境中,資料環境(Data Environment)是個重要的問題,其中共用變數和私人變數是根本。
多線程編程模型中,線程間的全域變數和靜態變數是共用的,而局部變數是私人的。但是對於OpenMP編程而言,預設變數往往是共用變數,而不管它是全域靜態變數還是局部自動變數,也就是說OpenMP各個線程的變數是共用還是私人,是根據OpenMP自身的規則和相關的資料子句而定,而不是依據作業系統線程或者進程上變數的特性而定的。
OpenMP的資料處理子句包括private,firstprivate,lastprivate,shared,default,reduction,copyin和copyprivate。這些資料處理子句與編譯制導指令parallel、for和sections相結合,用來控制變數的作用範圍。下面介紹在並列區域內的資料的私人性。
(1)局部變數是線程私人的嗎?
上文提到OpenMP中線程變數是共用的還是私人的是根據OpenMP自身的規則和相關的資料子句而定,那麼在一個並行域中生命的局部變數是線程私人嗎?是,可以記住這樣一條結論:OpenMP編程中,並行域中聲明的變數是線程私人的。下面是一個證明此條結論的小程式
#pragma omp parallel forfor(i=0; i<20;i++){ int seed; cout<<dl;seed++<<"TNO:"<<omp_get_thread_num()<<endl;}
這段程式的輸出如下:
0 TNO. 20 TNO. 20 TNO. 100 TNO. 60 TNO. 50 TNO. 10 TNO. 130 TNO. 12......
seed值一直都是0。OpenMP並行域中聲明的變數和多線程編程可以認為是相同的。
(2)private、firstprivate、lastprivate、threadprivate、copyprivate系列
還是先說用法:private子句用於講一個或者多個變數聲明成線程的私人變數,每個線程都有該變數副本,線程間的該變數副本相互獨立,private聲明的變數初始值在並行域入口處是未定義的;firstprivate和lastprivate子句的語義和private子句差不多(確切地說,firstprivate子句和lastprivate子句所表達的語義是private表達語義的超集),差別在於,firstprivate聲明的私人變數由主線程中的變數初始化,lastprivate子句用來實現在退出並行域將私人變數的值賦值給共用變數(並行域外和lastprivate聲明的變數同名的變數)。
以下面一段代碼說明:
碼一:
int k = 100;#pragma omp parallel for private(k)for(i=0;i<10;i++){ cout<<k<<" TNO. "<<omp_get_thread_num()<<endl;}cout<<k<<end;
程式輸出為:
3 TNO. 13 TNO. 112 TNO. 814 TNO.10.........100
解釋:k被private子句聲明為線程的私人變數,每個線程都有個副本,這個副本與其他線程的k無關,也與進程的k無關。從結果看,每個線程的k都不同,也沒有規律,因為k沒有被初始化,最後輸出的k和之前聲明的一樣,說明進程的k和線程k也無關,雖然都是k。
碼二:
int k = 100;#pragma omp parallel for firstprivate(k),lastprivate(k)for(k=0;k<20;k++){ k=k+i; cout<<k<<" TNO. "<<omp_get_thread_num()<<endl;}cout<<k<<endl;
102 TNO. 1105 TNO. 1119 TNO. 15113 TNO. 9.........119
解釋:firstprivate子句將k初始化為進程的k,於是每個進程的k都被初始化成了100。最後一句cout顯示k是119,歸功於lastprivate語句,進程的k值被賦值成最後一個線程(文法上的最後一個,並不是實際運行時的最後一個)的k值。
(3)threadprivate和copyprivate