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;
标签:include,num,int,C++,omp,线程,OpenMP,TBB,parallel From: https://www.cnblogs.com/ybqjymy/p/17480129.html