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原语),从而保证程序对临界资源的互斥访问。