如果你想要變量是每個(gè)線程擁有自己的單獨(dú)的變量梯醒,那么就在并行塊里面定義變量宽堆,對于在進(jìn)入并行塊前已經(jīng)定義了的變量,是在多個(gè)線程之間共享的茸习,需要小心處理畜隶,否則程序不會按照我們想要的方式執(zhí)行。
在上面這個(gè)程序中号胚,試圖用sum來累加變量和籽慢。#pragma omp parallel for告訴編譯器要把這個(gè)for語句拆開來并行執(zhí)行猫胁。注意 sum 定義在并行塊外面弃秆,所以在并行塊中會共享這個(gè)變量。也就是說在四個(gè)核中用的是同一個(gè)變量盼樟,對于sum + = i 語句晨缴,是先把sum的值取出來和 i 相加后再把結(jié)果賦給sum變量峡捡。既然四個(gè)線程用的是同一個(gè)sum,那么如果sum在線程0中取出來了阁吝,另一個(gè)線程中也同時(shí)取出來了同樣值的sum械拍,加完后值在兩個(gè)線程中都會被送就回到sum坷虑,那么就會造成有一次操作被重寫了(數(shù)據(jù)碰撞),也就是說最后的結(jié)果很可能小于正確的結(jié)果定躏。(實(shí)際上我在visual studio2019上運(yùn)行上面的代碼并沒有出錯(cuò)芹敌,但是不排除是因?yàn)関s進(jìn)行了更正操作)結(jié)果如下:
為了避免上面中出現(xiàn)的情況碧聪,可以進(jìn)行一些其他操作:比如可以在 sum += i; 前面加上#pragma omp critical 語句液茎,這條語句是告訴編譯器下面的代碼塊很關(guān)鍵豁护,任何時(shí)刻都只能有一個(gè)線程執(zhí)行該代碼塊欲间。不過這樣的話代碼會在多個(gè)核中的運(yùn)行會相互影響猎贴,對性能有一定影響她渴,最好的解決方法是用規(guī)約reduction,代碼如下圖:
reduction (+:sum) 告訴編譯器,這并行塊要用規(guī)約满葛,+表示求和操作,sum是目標(biāo)變量名篇亭。這樣的話译蒂,編譯器就會給每個(gè)線程一個(gè)sum的拷貝并正確初始化為0谊却,然后每個(gè)線程執(zhí)行完之后再合并因惭。規(guī)約只適用于固定的操作符。
OpenMP支持的規(guī)約操作符如下圖:
如果我們不想要已經(jīng)定義了的變量在多個(gè)線程中共享,想要讓每個(gè)線程有自己的拷貝怎么辦呢乒躺?下面就簡單介紹一個(gè)private從句嘉冒,以及firstprivate和lastprivate咆繁。通過使用#pragma omp parallel private(variable list),告訴編譯器在下面的并行塊中每個(gè)線程都對variable list列出的變量進(jìn)行拷貝。不過值得注意的時(shí)坏为,每個(gè)線程中拷貝的變量的初始值是不確定的匀伏,執(zhí)行完并行塊后該變量的值也不確定。所以就有了firstprivate和lastprivate熙侍,firstprivate 是說變量的初始值就是按照該變量進(jìn)入并行塊之前的值初始化蛉抓;lastprivate 是說退出并行塊后芝雪,按照串行情況最后的一個(gè)值給到主線程中的變量。如果同時(shí)使用firstprivate和lastprivate位岔,那么該變量就會有和串行是一樣的初始值和結(jié)束值抒抬。