首页 > 编程语言 >C++ OpenMP、TBB库的简单使用

C++ OpenMP、TBB库的简单使用

时间:2023-06-14 14:35:37浏览次数:39  
标签:include num int C++ omp 线程 OpenMP TBB parallel

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关键字 异常关键字 —>返回类型 {函数体} (实参)

 

  1. 捕获子句([]是lambda表达式的先导符号,告诉编译器下面是lambda表达式)

  2. 参数列表(可选)

  3. mutable关键字(可选)

  4. 异常关键字(可选)

  5. 返回类型(可选)(省略返回类型时,->也要省略)

  6. 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

相关文章

  • C++ 中的运算符重载
     您可以重定义或重载大部分C++内置的运算符。这样,您就能使用自定义类型的运算符。重载的运算符是带有特殊名称的函数,函数名是由关键字operator和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。https://www.tzffs.com/lnzt15/......
  • C++ 多态
     多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。下面的实例中,基类Shape被派生为两个类https://www.tzffs.com/mnst14/......
  • C++ 模板类编译过程中出现“undefined reference to”问题
    问题描述C++在使用模板(template)类的时候,如果将类的成员函数的声明和实现分别放在.h头文件和.cpp源文件中,编译时会报错undefinedreferencexxx,找不到对应成员函数。起因.h文件中类的声明为://线程池,定义成模板类,为了代码的复用template<typenameT>classThreadPool{......
  • C/C++《程序设计课程设计》[2023-06-14]
    C/C++《程序设计课程设计》[2023-06-14]《程序设计课程设计》指导书程序设计课程设计说明书一、设计任务与要求《程序设计课程设计》是在完成《程序设计基础》课程学习后进行的一门专业实践课程,是培养学生综合运用所学知识解决专业相关问题的重要环节,是对学生实际工作能力的......
  • 配置 Sublime Text4为 C++ 编辑器
    概述涉及以下插件的安装和配置PackageControlTerminusLSPLSP-clangdclang-formatLSP-pyrightLSP-json配置sublime安装PackageControl以进行包管理。Terminus安装Terminus以实现sublimetext4内的terminal。绑定快捷键:[ { "keys":[ "ctrl+shift+t" ], "c......
  • C++面试八股文:C++中,函数的参数应该传值还是传引用?
    C++面试八股文:C++中,函数的参数应该传值还是传引用?某日二师兄参加XXX科技公司的C++工程师开发岗位第8面:面试官:C++中,函数的参数应该传值还是传引用?二师兄:要看参数的用途。如果是出参,必须传引用。如果是入参,主要考虑参数类型的大小,来决定传值还是传引用。面试官:为什么不使用......
  • C++面试八股文:什么是RAII?
    某日二师兄参加XXX科技公司的C++工程师开发岗位第13面:面试官:什么是RAII?二师兄:RAII是ResourceAcquisitionIsInitialization的缩写。翻译成中文是资源获取即初始化。面试官:RAII有什么特点和优势?二师兄:主要的特点是,在对象初始化时获取资源,在对象析构时释放资源。这种技术可以......
  • C++面试八股文:什么是RAII?
    某日二师兄参加XXX科技公司的C++工程师开发岗位第13面:面试官:什么是RAII?二师兄:RAII是ResourceAcquisitionIsInitialization的缩写。翻译成中文是资源获取即初始化。面试官:RAII有什么特点和优势?二师兄:主要的特点是,在对象初始化时获取资源,在对象析构时释放资源。这种技术可以......
  • 《C++》类和对象
    C++面向对象的三大特性:封装、继承、多态封装将属性和行为作为一个整体,表现生活中的事物。对属性和行为加以权限控制。classCompute{ //访问权限public: intAdd(intx,inty) { returnx+y; } intSub(intx,inty) { returnx+y; } intMul(intx,inty) {......
  • 现代C++学习指南-方向篇
    C++是一门有着四十年历史的语言,先后经历过四次版本大升级(诞生、98、11、17(20),14算小升级)。每次升级都是很多问题和解决方案的取舍。了解这些历史,能更好地帮助我们理清语言的发展脉络。所以接下来我将借它的发展历程,谈一谈我对它的理解,最后给出我认为比较合理的学习路线指南。C++0......