OpenMP中的數據處理子句
10.1.1 private子句
private子句用于將一個或多個變量聲明成線程私有的變量,變量聲明成私有變量后,指定每個線程都有它自己的變量私有副本,其他線程無法訪問私有副本。即使在并行區域外有同名的共享變量,共享變量在并行區域內不起任何作用,并且并行區域內不會操作到外面的共享變量。
private子句的用法格式如下:
private(list)
下面便是一個使用private子句的代碼例子:
- int k = 100;
- #pragma omp parallel for private(k)
- for ( k=0; k < 10; k++)
- {
- printf("k=%d/n", k);
- }
- printf("last k=%d/n", k);
- 上面程序執行后打印的結果如下:
- k=6
- k=7
- k=8
- k=9
- k=0
- k=1
- k=2
- k=3
- k=4
- k=5
- last k=100
從打印結果可以看出,for循環前的變量k和循環區域內的變量k其實是兩個不同的變量。
用private子句聲明的私有變量的初始值在并行區域的入口處是未定義的,它并不會繼承同名共享變量的值。
出現在reduction子句中的參數不能出現在private子句中。
10.1.2 firstprivate子句
private聲明的私有變量不能繼承同名變量的值,但實際情況中有時需要繼承原有共享變量的值,OpenMP提供了firstprivate子句來實現這個功能。
先看一下以下的代碼例子
- int k = 100;
- #pragma omp parallel for firstprivate(k)
- for ( i=0; i < 4; i++)
- {
- k+=i;
- printf("k=%d/n",k);
- }
- printf("last k=%d/n", k);
上面代碼執行后打印結果如下:
k=100
k=101
k=103
k=102
從打印結果可以看出,并行區域內的私有變量k繼承了外面共享變量k的值100作為初始值,并且在退出并行區域后,共享變量k的值保持為100未變。
10.1.3 lastprivate子句
有時在并行區域內的私有變量的值經過計算 后,在退出并行區域時,需要將它的值賦給同名的共享變量,前面的private和firstprivate子句在退出并行區域時都沒有將私有變量的最后取 值賦給對應的共享變量,lastprivate子句就是用來實現在退出并行區域時將私有變量的值賦給共享變量。
舉個例子如下:
- int k = 100;
- #pragma omp parallel for firstprivate(k),lastprivate(k)
- for ( i=0; i < 4; i++)
- {
- k+=i;
- printf("k=%d/n",k);
- }
- printf("last k=%d/n", k);
上面代碼執行后的打印結果如下:
- k=100
- k=101
- k=103
- k=102
- last k=103
從打印結果可以看出,退出for循環的并行區域后,共享變量k的值變成了103,而不是保持原來的100不變。
由于在并行區域內是多個線程并行執行的, 最后到底是將那個線程的最終計算結果賦給了對應的共享變量呢?OpenMP規范中指出,如果是循環迭代,那么是將最后一次循環迭代中的值賦給對應的共享變 量;如果是section構造,那么是最后一個section語句中的值賦給對應的共享變量。注意這里說的最后一個section是指程序語法上的最后一 個,而不是實際運行時的最后一個運行完的。
如果是類(class)類型的變量使用在 lastprivate參數中,那么使用時有些限制,需要一個可訪問的,明確的缺省構造函數,除非變量也被使用作為firstprivate子句的參數; 還需要一個拷貝賦值操作符,并且這個拷貝賦值操作符對于不同對象的操作順序是未指定的,依賴于編譯器的定義。
#p#
10.1.4 threadprivate子句
threadprivate子句用來指定全局的對象被各個線程各自復制了一個私有的拷貝,即各個線程具有各自私有的全局對象。
用法如下:
- #pragma omp threadprivate(list) new-line
下面用threadprivate命令來實現一個各個線程私有的計數器,各個線程使用同一個函數來實現自己的計數。計數器代碼如下:
- int counter = 0;
- #pragma omp threadprivate(counter)
- int increment_counter()
- {
- counter++;
- return(counter);
- }
如果對于靜態變量也同樣可以使用threadprivate聲明成線程私有的,上面的counter變量如改成用static類型來實現時,代碼如下:
- int increment_counter2()
- {
- static int counter = 0;
- #pragma omp threadprivate(counter)
- counter++;
- return(counter);
- }
threadprivate和private的區別在于threadprivate聲明的變量通常是全局范圍內有效的,而private聲明的變量只在它所屬的并行構造中有效。
threadprivate的對應只能用于copyin,copyprivate,schedule,num_threads和if子句中,不能用于任何其他子句中。
用作threadprivate的變量的地址不能是常數。
對于C++的類(class)類型變量,用作threadprivate的參數時有些限制,當定義時帶有外部初始化時,必須具有明確的拷貝構造函數。
對于windows系統,threadprivate不能用于動態裝載(使用LoadLibrary裝載)的DLL中,可以用于靜態裝載的DLL中,關于windows系統中的更多限制,請參閱MSDN中有關threadprivate子句的幫助材料。
有關threadprivate命令的更多限制方面的信息,詳情請參閱OpenMP2.5規范。
shared子句用來聲明一個或多個變量是共享變量。
用法如下:
shared(list)
需要注意的是,在并行區域內使用共享變量時,如果存在寫操作,必須對共享變量加以保護,否則不要輕易使用共享變量,盡量將共享變量的訪問轉化為私有變量的訪問。
循環迭代變量在循環構造區域里是私有的。聲明在循環構造區域內的自動變量都是私有的。
default子句用來允許用戶控制并行區域中變量的共享屬性。
用法如下:
default(shared | none)
使用shared時,缺省情況下,傳入并行區域內的同名變量被當作共享變量來處理,不會產生線程私有副本,除非使用private等子句來指定某些變量為私有的才會產生副本。 如果使用none作為參數,那么線程中用到的變量必須顯示指定是共享的還是私有的,除了那些由明確定義的除外。
reduction子句主要用來對一個或多個參數條目指定一個操作符,每個線程將創建參數條目的一個私有拷貝,在區域的結束處,將用私有拷貝的值通過指定的運行符運算,原始的參數條目被運算結果的值更新。
reduction子句用法如下:
reduction(operator:list)
下表列出了可以用于reduction子句的一些操作符以及對應私有拷貝變量缺省的初始值,私有拷貝變量的實際初始值依賴于redtucion變量的數據類型。
表10-4-1:reduction操作中各種操作符號對應拷貝變量的缺省初始值
|
Operator |
Initialization value |
| + | 0 |
| * |
1
|
| - | 0 |
| & | ~0 |
| | | 0 |
| ^ | 0 |
| && | 1 |
| || | 0 |
例如一個整數求和的程序如下:
- int i, sum = 100;
- #pragma omp parallel for reduction(+: sum)
- for ( i = 0; i < 1000; i++ )
- {
- sum += i;
- }
- printf( "sum = %ld/n", sum);
注意,如果在并行區域內不加鎖保護就直接對共享變量進行寫操作,存在數據競爭問題,會導致不可預測的異常結果。共享數據作為private、firstprivate、lastprivate、threadprivate、reduction子句的參數進入并行區域后,就變成線程私有了,不需要加鎖保護了。
#p#
copyin子句用來將主線程中threadprivate變量的值拷貝到執行并行區域的各個線程的threadprivate變量中,便于線程可以訪問主線程中的變量值,
用法如下:
- copyin(list)
copyin中的參數必須被聲明成threadprivate的,對于類類型的變量,必須帶有明確的拷貝賦值操作符。
對于前面threadprivate中講過的計數器函數,如果多個線程使用時,各個線程都需要對全局變量counter的副本進行初始化,可以使用copyin子句來實現,示例代碼如下:
- int main(int argc, char* argv[])
- {
- int iterator;
- #pragma omp parallel sections copyin(counter)
- {
- #pragma omp section
- {
- int count1;
- for ( iterator = 0; iterator < 100; iterator++ )
- {
- count1 = increment_counter();
- }
- printf("count1 = %ld/n", count1);
- }
- #pragma omp section
- {
- int count2;
- for ( iterator = 0; iterator < 200; iterator++ )
- {
- count2 = increment_counter();
- }
- printf("count2 = %ld/n", count2);
- }
- }
- printf("counter = %ld/n", counter);
- }
打印結果如下:
count1 = 100
count2 = 200
counter = 0
copyprivate子句提供了一種機制用一個私有變量將一個值從一個線程廣播到執行同一并行區域的其他線程。
用法如下:
copyprivate(list)
copyprivate子句可以關聯single構造,在single構造的barrier到達之前就完成了廣播工作。copyprivate可以對private和threadprivate子句中的變量進行操作,但是當使用single構造時,copyprivate的變量不能用于private和firstprivate子句中。
下面便是一個使用copyprivate的代碼例子:
- int counter = 0;
- #pragma omp threadprivate(counter)
- int increment_counter()
- {
- counter++;
- return(counter);
- }
- #pragma omp parallel
- {
- int count;
- #pragma omp single copyprivate(counter)
- {
- counter = 50;
- }
- count = increment_counter();
- printf("ThreadId: %ld, count = %ld/n", omp_get_thread_num(), count);
- }
打印結果為:
ThreadId: 2, count = 51
ThreadId: 0, count = 51
ThreadId: 3, count = 51
ThreadId: 1, count = 51
如果沒有使用copyprivate子句,那么打印結果為:
ThreadId: 2, count = 1
ThreadId: 1, count = 1
ThreadId: 0, count = 51
ThreadId: 3, count = 1
從打印結果可以看出,使用copyprivate子句后,single構造內給counter賦的值被廣播到了其他線程里,但沒有使用copyprivate子句時,只有一個線程獲得了single構造內的賦值,其他線程沒有獲取single構造內的賦值。
原文鏈接:http://blog.csdn.net/drzhouweiming/article/details/2033276

























