文章目录
Notice
使用openmp进行多线程开发,包含一些简单的用法,不包含一些比较难的概念。
使用GCC进行编程,重中之重,编译的时候要加-openmp打开openmp
#pragma…{}宣布大括号里面是并行的
g++ -fopenmp .\test2.cpp -o test
输出不要使用cout,使用printf,不然你的每一行都是乱序,根本没法看。
HelloWorld
最简单的helloworld代码,自动分配线程数量,我的电脑两核四线程,就会有四个线程一起执行。
#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
#pragma omp parallel
{
printf("hello,world\n");
}
return 0;
}
------------------------------------------
hello,world
hello,world
hello,world
hello,world
自己分配线程数
两种方式,把#pragma后面加代表作用域内6个线程,也可以使用我注释掉的那行,代表统一使用六个。那么我明明只有四线程,为什么可以申请六个呢?
这里要搞清楚并行和并发的概念,并行代表真的所有线程同时执行,我四核,最多也就并行4,速度四倍。而并发只是我们看上去在同时进行,就像你同时写两份作业,这写一道那写一道,看上去好像差不多时间完成,但实际上你要花的时间可能比先写完数学再写完英语更加长。但是有的时候我们必须这样,就像你有很多门课,你并不是学完一门换下一门,而是学学这门学学那门,这就是并发。
#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
//omp_set_num_threads(6);
#pragma omp parallel num_threads(6)
{
printf("hello,world\n");
}
return 0;
}
---------------------------------------
hello,world
hello,world
hello,world
hello,world
hello,world
hello,world
获取进程号
使用omp_get_thread_num()
获取目前是那个线程,你可以发现输出顺序不同,多执行几次也不会一样,因为每个线程速度是不一样的。
#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
omp_set_num_threads(6);
#pragma omp parallel
{
int id=omp_get_thread_num();
printf("%d\n",id);
}
return 0;
}
------------------------
3
1
2
0
4
5
你还可以用omp_get_num_threads()
获得一共几个线程
加速For循环
两种写法,看到直接使用#pragma omp parallel for
后面就不能跟中括号了,要紧跟for
#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
#pragma omp parallel
{
#pragma omp for
for(int i=1;i<=10;i++)
{
int id = omp_get_thread_num();
printf("thread: %d num: %d\n",id,i);
}
}
return 0;
}
#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
#pragma omp parallel for
for(int i=1;i<=10;i++)
{
int id = omp_get_thread_num();
printf("thread: %d num: %d\n",id,i);
}
return 0;
}
-------------------------
thread: 0 num: 1
thread: 0 num: 2
thread: 1 num: 4
thread: 3 num: 9
thread: 3 num: 10
thread: 0 num: 3
thread: 1 num: 5
thread: 1 num: 6
thread: 2 num: 7
thread: 2 num: 8
有序for
注意这种写法,不是在最外层for加关键词就够了,for里面也要加,另外里面#pragma omp ordered
之前都还是并行的,但是在之后就都是串行了,即便是我打的内部大括号后面。所以一般就是为了输出有序
#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
#pragma omp parallel for ordered
for(int i=1;i<=10;i++)
{
#pragma omp ordered
{
int id = omp_get_thread_num();
printf("thread: %d num: %d\n",id,i);
}
}
return 0;
}
-----------------------------------
thread: 0 num: 1
thread: 0 num: 2
thread: 0 num: 3
thread: 1 num: 4
thread: 1 num: 5
thread: 1 num: 6
thread: 2 num: 7
thread: 2 num: 8
thread: 3 num: 9
thread: 3 num: 10
schedule(for)
#pragma omp for schedule(static, chunk)
静态每chunk个循环分给一个线程,我也不确定有啥用
#pragma omp for schedule(dynamic, chunk)
动态划分,可能第一个chunk给1,第二个给2,分第三个的时候第一个已经好了再给1
sections,并行执行几个section
不保证顺序
#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
#pragma omp parallel sections
{
#pragma omp section
{
printf("xxx\n");
}
#pragma omp section
{
printf("yyy\n");
}
#pragma omp section
{
printf("zzz\n");
}
}
return 0;
}
-------------------------------
xxx
yyy
zzz
barrier(所有线程到这停)
在#pragma omp barrier
前所有进程结束再继续,for和sections都隐含了barrier
#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
#pragma omp parallel
{
int id=omp_get_thread_num();
printf("id: %d\n",id);
#pragma omp barrier
if(id==1)
{
printf("111\n");
}
}
return 0;
}
-------------------------
id: 2
id: 0
id: 1
id: 3
111
nowait(线程干完就往后走,不等其他线程)
让for和sections不等到全都结束就继续
#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
#pragma omp parallel
{
#pragma omp for nowait
for(int i=0;i<=6;i++)
{
printf("%d\n",i);
}
printf("xxx\n");
}
return 0;
}
--------------------------------------------
0
1
xxx
6
xxx
4
5
2
3
xxx
single(只执行一次)
以下代码只有一个线程执行,显然没有这行,会有四个continue
#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
#pragma omp parallel
{
#pragma omp for
for(int i=0;i<=3;i++)
{
printf("%d\n",i);
}
#pragma omp single
printf("continue\n");
#pragma omp for
for(int i=0;i<=3;i++)
{
printf("%d\n",i);
}
}
return 0;
}
--------------------------------
0
2
1
3
continue
2
1
3
0
critical
critical里面只能由一个线程执行,相同的critical变成串行
所有没名字的critical都是一个critical
#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
#pragma omp parallel sections
{
#pragma omp section
{
#pragma omp critical (critical1)
{
for (int i=0; i < 5; i++)
{
printf("section1 thread %d excute i = %d\n", omp_get_thread_num(), i);
Sleep(200);
}
}
}
#pragma omp section
{
#pragma omp critical (critical1)
{
for (int j=0; j < 5; j++)
{
printf("section2 thread %d excute j = %d\n", omp_get_thread_num(), j);
Sleep(200);
}
}
}
}
return 0;
}
atomic(声明同一时间一个线程运算此单元)
不加atomic那行,x最终会少。x++包含拿出x,x++,放回去两个操作,可能有线程拿出x还没放回去,另一个就去拿了,那就会少加。
#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
int x=0;
#pragma omp parallel num_threads(6)
{
for(int i=0; i<100000; ++i)
#pragma omp atomic
x++;
printf("%d", x);
}
数据作用域
Private
private(x) x分别被每一个线程拥有,离开作用域后变回原来的值,就和在里面重新定义了一个是一样的。
#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
int x=10086;
#pragma omp parallel private(x)
{
x=omp_get_thread_num();
printf("%d\n",x);
}
printf("finished:%d\n",x);
return 0;
}
------------------------------
1
0
2
3
finished:10086
firstprivate and lastprivate
其实我不太会用,,我宁可里面开新变量,后面再加一个barrier,然后付给外变量
#pragma omp parallel firstprivate(i) private(a)
i会使用外层i作为初值
#pragma omp sections lastprivate(i) private(a)
会把最后一次迭代的i传回去
shared
大家都是同一个a,b#pragma omp parallel shared(a,b)
,要注意冲突
default
没看懂
reduction
分成几个线程,每个线程进行sum++之类的操作时候,可能冲突,它现在就是让各自加各自的sum,最后一起加起来,只有这样才能这么快。
#include "iostream"
#include <omp.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
int main()
{
omp_set_num_threads(2);
long long sum=0;
clock_t t1=clock();
#pragma omp parallel for reduction(+:sum)
for(long i=1;i<=100000000;i++)
sum=sum+i;
clock_t t2=clock();
printf("sum=%lld\n",sum);
printf("parallel time=%d\n",(t2-t1));
sum=0;
t1=clock();
for(long i=1;i<=100000000;i+=1)
sum=sum+i;
t2=clock();
printf("sum=%lld\n",sum);
printf("serial time=%d\n",(t2-t1));
system("pause");
return 0;
}
-----------------
sum=500000000500000000
parallel time=1357
sum=500000000500000000
serial time=2530