1.OpenMP的简单使用
OpenMP可以用来并行计算for循环,提高程序运行速度。
首先要打开OpenMP支持:“配置属性”——“C/C++”——“语言”——“1.OpenMP支持”后选择“是”。
1 omp_get_num_procs() //获取系统中处理器的个数
2 omp_set_num_threads(num_count) //设置线程数
3 omp_get_thread_num() //获取当前线程的Id号
用法1:
1 #pragma omp parallel
2 {
3 //每个线程都会执行大括号里的代码
4 //并行的线程数由系统决定
5 }
6 //指定执行代码块的线程数
7 omp_set_num_threads(4);//设置线程数,需要包含头文件<omp.h>
8 #pragma omp parallel
9 或者
10 #pragma omp parallel num_threads(4)//在parallel后直接增加线程子句
11 {
12 //执行该代码块的线程数为4
13 }
例子1:
1 #include <iostream>
2 #include "omp.h"
3 using namespace std;
4 int main()
5 {
6 omp_set_num_threads(4);//设置线程数
7 #pragma omp parallel
8 {
9 //cout << "hello ,threads" << omp_get_thread_num() << endl;//cout打印格式混乱
10 printf("I am Thread %d\n", omp_get_thread_num());
11 }
12 system("pause");
13 return 0;
14 }
从运行结果可以看出,各线程的先后顺序不确定(0是主线程)。
用法2:
1 #pragma omp parallel for num_threads(4)
2 for( ; ; )
3 {
4 //并行计算for循环里的代码块,循环退出,并行结束
5 }
6 或
7 #pragma omp parallel
8 {
9 #pragma omp for
10 for()
11 {
12 //并行运行该代码块
13 }
14 }
例子2.
1 #include <iostream>
2 #include "omp.h"
3 using namespace std;
4 int main()
5 {
6 #pragma omp parallel for num_threads(4)//只需要多加这一行代码
7 for (int i = 0; i < 20; i++)
8 {
9 //cout << "hello ,threads :" << omp_get_thread_num() << endl;
10 printf("i = %d, I am Thread %d\n", i, omp_get_thread_num());
11 }
12 system("pause");
13 return 0;
14 }
运行结果:
for循环执行的先后顺序不能保证,因此for循环里的内容必须不存在依赖关系。for循环中不能有break和return退出出口。
数据同步问题:
例3.
1 #include <iostream>
2 #include "omp.h"
3 using namespace std;
4 int main()
5 {
6 int n = 10000;
7 int sum = 0;
8 omp_set_num_threads(4);
9 #pragma omp parallel
10 {
11 #pragma omp for
12 for (int i = 0; i < n; i++)
13 {
14 sum += 1;
15 }
16 }
17 cout << " sum = " << sum << endl;
18 system("pause");
19 return 0;
20 }
正确答案应为10000,但运行时每次得到的结果都不同,这是因为sum变量是每个线程共享的,多个线程同时对sum操作,会因为数据同步问题发生错误。
解决方法:
方法一:对操作共享变量的代码段做同步标识
1 int main()
2 {
3 int n = 10000;
4 int sum = 0;
5 omp_set_num_threads(4);
6 #pragma omp parallel
7 {
8 #pragma omp for
9 for (int i = 0; i < n; i++)
10 {
11 #pragma omp critical //critical用在一段代码临界区之前,保证每次只有一个OpenMP线程进入
12 sum += 1;
13 }
14 }
15 }
方法二:每个线程拷贝一份sum变量,退出并行块时再把各个线程的sum相加
1 #pragma omp parallel
2 {
3 #pragma omp for reduction(+:sum)
4 for (int i = 0; i < n; i++) {
5 {
6 sum += 1;
7 }
8 }
9 }
2.TBB库的简单使用
TBB(Thread Building Blocks)是英特尔发布的一个C++标准库。
1.配置
设置环境变量:将tbb中bin文件夹下的vc12(与VS2013对应)的路径添加到环境变量,然后重启电脑。
将include文件夹所在路径添加到包含目录,将lib文件夹所在路径添加到库目录。
2.parallel_for的用法
包含头文件#include ”tbb/parallel_for.h“
**parallel_for(range, body, partitioner)提供了并行迭代的泛型形式。它表示在区域的每个值,并行执行body。partitioner选项指定了分割策略。
例子:
1 #include <iostream>
2 #include <tbb/tbb.h>
3 using namespace std;
4 using namespace tbb;
5 typedef vector<int>::iterator IntVecIt;
6 struct body
7 {
8 //blocked_range<T>是ttb库模板类,声明一个一维迭代空间
9 //parallel_for要求实体对象的operator()声明为const
10 void operator()(const blocked_range<IntVecIt>&r)const//重载()运算符
11 {
12 for (auto i = r.begin(); i != r.end(); i++)
13 cout << *i << ' ';
14 }
15 };
16 int main()
17 {
18 vector<int> vec;
19 for (int i = 0; i<10; i++)
20 vec.push_back(i);
21 parallel_for(blocked_range< IntVecIt>(vec.begin(), vec.end()), body());
22 system("pause");
23 return 0;
24 }
使用lambda表达式,上面的例子可以写为
1 int main()
2 {
3 vector<int> vec;
4 for (int i = 0; i<10; i++)
5 vec.push_back(i);
6 parallel_for((blocked_range< IntVecIt>(vec.begin(), vec.end())),
7 [&](const blocked_range<IntVecIt>&r){
8 for (auto i = r.begin(); i != r.end(); i++)
9 cout << *i << " ";
10 }
11 );
12 system("pause");
13 return 0;
14 }
为了更紧凑,对于在一个整形的连续区域执行并行循环,TBB有对应形式的 parallel_for 。表达式 parallel_for(first,last,step,f) 就像 for(auto i = first; i< last; i+= step) f(i)
3.运行时间对比
1 #include <iostream>
2 #include <time.h>
3 void test()
4 {
5 int a = 0;
6 for (int i = 0; i<100000000; i++)
7 a++;
8 }
9 int main()
10 {
11 clock_t t1 = clock();
12 //OpenMP多线程
13 #pragma omp parallel for
14 for (int i = 0; i < 8; i++)
15 test();
16 clock_t t2 = clock();
17 std::cout << "time: " << t2 - t1 << std::endl;//打印运行时间
18 system("pause");
19 }
20 //tbb多线程
21 parallel_for(blocked_range<size_t>(0, 8), [&](const blocked_range<size_t> &i){
22 test();
23 });
对于上面这个例子,调用8次test函数,不使用多线程的运行时间为1333ms,使用OpenMP多线程后的运行时间为196ms,使用tbb多线程后的运行时间为203ms。可以看出使用OpenMP和tbb后程序运行时间加快了好几倍。
3.thread多线程的简单使用
包含头文件#include
创建多线程
1 void func01()
2 {
3 //to do something
4 }
5
6 void func02(int num)
7 {
8 //to do something
9 }
10 int main()
11 {
12 thread t1(func01);//给函数func创建子线程
13 t1.join();//阻塞主线程(main函数的线程称为主线程)
14 //t1.detach();//使子线程从主线程中分离,不会阻塞主线程
15 //带参子线程
16 thread t2(func02,5); t2.join();
17 }
18
19 //线程互斥
20 #include <iostream>
21 #include <thread>
22 #include <Windows.h>
23 #include <mutex>// mutex类的使用需要包含该头文件
24 using namespace std;
25 mutex mu; //线程互斥对象
26
27 int totalNum = 100;
28
29 void thread01()
30 {
31 while (totalNum > 0)
32 {
33 mu.lock(); //同步数据锁
34 cout << totalNum << endl;
35 totalNum--;
36 Sleep(100);
37 mu.unlock(); //解除锁定
38 }
39 }
40 void thread02()
41 {
42 while (totalNum > 0)
43 {
44 mu.lock();
45 cout << totalNum << endl;
46 totalNum--;
47 Sleep(100);
48 mu.unlock();
49 }
50 }
51
52 int main()
53 {
54 thread task01(thread01);
55 thread task02(thread02);
56 task01.detach();
57 task02.detach();
58 system("pause");
59 }
60
61 //在类外部为类的成员函数创建子线程
62 class Test
63 {
64 public:
65 void add(int a,int b,int &c)//要改变c的值,用引用接收
66 {
67 c=a+b;
68 }
69 private:
70 int a=1;
71 int b=2;
72 int c=0;
73 };
74 int main()
75 {
76 Test T;
77 //需要传递对象T,要改变主线程中的值,则需要传递引用,thread中用ref,用&会报错
78 thread t1(&Test::add,&T,T.a,T.b,ref(T.c));
79 cout <<T.c<<endl;
80 }
4.lambda表达式
lambda表达式是C++11引入的新特性,功能:定义并创建匿名函数对象,以简化编程工作。
语法:[捕获子句](参数列表)mutable关键字 异常关键字 —>返回类型 {函数体} (实参)
-
捕获子句([]是lambda表达式的先导符号,告诉编译器下面是lambda表达式)
-
参数列表(可选)
-
mutable关键字(可选)
-
异常关键字(可选)
-
返回类型(可选)(省略返回类型时,->也要省略)
-
lambda函数体
1 int num = [](int num)->int{return ++num; }(10);//10是传递的实参
2 cout << num << endl;//打印结果为11
如果想在lambda表达式内访问外部的数据,可使用以下方式传递变量:
1 [=]//值传递所有变量
2 [&]//引用所有变量
3 [var]//值传递变量
4 [&var]//引用传递变量
例子
1 int num = 10;
2 [=]()mutable //当使用mutable关键字时,可以对值传递的变量进行“写”操作
3 {cout << ++num << endl;}();//此处的小括号不能省略
4 cout << num << endl; //调用lambda后num任然是10(值传递)
5
6 //写成函数的形式
7 auto fun = [=](int a)mutable ->int {return ++a;};//auto关键字自动推断lambda表达式的类型
8 cout << fun(num) << endl;
2.
1 如果想在lambda表达式内访问外部的数据,可使用以下方式传递变量:
2
3 ```c++
4 [=]//值传递所有变量
5 [&]//引用所有变量
6 [var]//值传递变量
7 [&var]//引用传递变量
例子
1 int num = 10;
2 [=]()mutable //当使用mutable关键字时,可以对值传递的变量进行“写”操作
3 {cout << ++num << endl;}();//此处的小括号不能省略
4 cout << num << endl; //调用lambda后num任然是10(值传递)
5
6 //写成函数的形式
7 auto fun = [=](int a)mutable ->int {return ++a;};//auto关键字自动推断lambda表达式的类型
8 cout << fun(num) << endl;