OpenMP是由OpenMP Architecture Review Board牽頭提出的,並已被廣泛接受,用於共享內存並行系統的多處理器程序設計的一套指導性編譯處理方案(Compiler Directive) 。OpenMP支持的編程語言包括C、C++和Fortran;而支持OpenMp的編譯器包括Sun Compiler,GNU Compiler和Intel Compiler等。OpenMP提供了對並行算法的高層的抽象描述,程序員通過在源代碼中加入專用的pragma來指明自己的意圖,由此編譯器可以自動將程序進行並行化,並在必要之處加入同步互斥以及通信。當選擇忽略這些pragma,或者編譯器不支持OpenMP時,程序又可退化爲通常的程序(一般爲串行),代碼仍然可以正常運作,只是不能利用多線程來加速程序執行。線程粒度和負載平衡等是傳統多線程程序設計中的難題,但在OpenMP中,OpenMP庫從程序員手中接管了部分這兩方面的工作。線程粒度和負載平衡等是傳統多線程程序設計中的難題,但在OpenMP中,OpenMP庫從程序員手中接管了部分這兩方面的工作。
VS可支持OpenMP,編寫OpenMP程序首先需要開啓OpenMP選項,並在代碼中包含“omp.h”頭文件。
測試OpenMP程序
1.首先編寫普通的for循環程序
#include <iostream>
using namespace std;
void demo1()
{
for (int i = 0; i < 10; i++)
{
cout << i << ",";
}
cout << endl;
}
int main(int argc, char* argv[])
{
for (int i = 0; i < 3; i++)
{
demo1();
}
return 0;
}
輸出結果爲有序序列:
0,1,2,3,4,5,6,7,8,9,
0,1,2,3,4,5,6,7,8,9,
0,1,2,3,4,5,6,7,8,9,
2.將for循環部分改寫爲OpenMP程序
#include <omp.h>
#include <iostream>
using namespace std;
void demo1()
{
#pragma omp parallel for
for (int i = 0; i < 10; i++)
{
cout << i << ",";
}
cout << endl;
}
int main(int argc, char* argv[])
{
//#pragma omp parallel for
for (int i = 0; i < 3; i++)
{
demo1();
}
return 0;
}
OpenMP會對#pragma omp parallel for修飾的for循環做併發處理,輸出結果爲無序序列(上述代碼中main函數裏的for串行執行):
3,4,5,8,9,0,6,7,1,2,
0,1,2,8,9,6,73,,4,5,
3,4,5,6,7,8,90,1,2,,
3.對代碼加鎖保證資源互斥訪問
#include <omp.h>
#include <iostream>
#include <mutex>
using namespace std;
void demo1()
{
static mutex lock;
lock.lock();
#pragma omp parallel for
for (int i = 0; i < 10; i++)
{
cout << i << ",";
}
cout << endl;
lock.unlock();
}
int main()
{
#pragma omp parallel for
for (int i = 0; i < 3; i++)
{
demo1();
}
return 0;
}
以上代碼對demo1內的資源加鎖,保證每個demo1作爲一個整體運行,最終結果如下:
0,1,2,3,4,5,6,7,8,9,
0,1,2,3,4,5,6,7,8,9,
0,1,2,3,4,5,6,7,8,9,
在這裏順便討論一下c++線程加鎖的問題,之前我的測試代碼裏編寫了這樣一個互斥類:
class xc_mutex
{
public:
xc_mutex()
{
while (1)
{
if (!m_bLock)
{
m_bLock = true;
break;
}
}
}
~xc_mutex()
{
m_bLock = false;
}
private:
static bool m_bLock;
};
bool xc_mutex::m_bLock = false;
使用以上類測試代碼時,顯然不能做到互斥訪問臨界資源,而c++11裏提供的mutex卻可以做到,這是什麼原因呢?
實際上,xc_mutex是在用戶態對互斥變量進行加鎖、解鎖操作,儘管語句精確到了“if(!m_bLock) m_bLock=true;”,但是這條if語句依然不是原子操作(可翻譯到其對應的彙編語句進行分析,至少需要翻譯成一個條件跳轉語句和一個寄存器賦值語句),因此當解鎖的瞬間,程序有一定機率跳轉到其它的語句執行,從而也跳出while循環;而std::mutex是在內核態對互斥變量進行加鎖、解鎖操作,雖然從用戶態切換到內核態需要消耗一定資源,但是它可以精確地實現原子操作(PV原語),從而保證程序對臨界資源的互斥訪問。