多线程的创建创建线程比较简单,C++提供头文件thread,使用std的thread实例化一个线程对象创建。
std::thread 在 #include 头文件中声明,因此使用 std::thread 时需要包含 #include 头文件。
#include <iostream> #include <thread> #include <stdlib.h> //sleep using namespace std; void t1() //普通的函数,用来执行线程 { for (int i = 0; i < 10; ++i) { cout << "t1111\n"; sleep(1); } } void t2() { for (int i = 0; i < 20; ++i) { cout << "t22222\n"; sleep(1); } } int main() { thread th1(t1); //实例化一个线程对象th1,使用函数t1构造,然后该线程就开始执行了(t1()) thread th2(t2); th1.join(); // 必须将线程join或者detach 等待子线程结束主进程才可以退出 th2.join(); cout << "here is main\n\n"; return 0; }
LIO-SAM中的线程操作
int main(int argc, char** argv) { ros::init(argc, argv, "lio_sam"); mapOptimization MO; ROS_INFO("\033[1;32m----> Map Optimization Started.\033[0m"); std::thread loopthread(&mapOptimization::loopClosureThread, &MO); std::thread visualizeMapThread(&mapOptimization::visualizeGlobalMapThread, &MO); ros::spin(); loopthread.join(); visualizeMapThread.join(); return 0; }
两个线程主要运行的函数主要时回环检测线程和全局地图可视化线程,在每个线程内部都有互斥锁操作防止两个线程运行时对相同数据进行干扰
互斥锁mutex是用来保证线程同步的,防止不同的线程同时操作同一个共享数据
通过mutex可以方便的对临界区域加锁,std::mutex类定义于mutex头文件,是用于保护共享数据避免从多个线程同时访问的同步原语。它提供了lock,try_lock,unlock等几个接口
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex m; int cnt = 10; void thread1() { while (cnt > 5){ m.lock(); if (cnt > 0) { --cnt; cout << cnt << endl; } m.unlock(); } } void thread2() { while (cnt > 0) { m.lock(); if (cnt > 0) { cnt -= 10; cout << cnt << endl; } m.unlock(); } } int main(int argc, char* argv[]) { thread th1(thread1); //实例化一个线程对象th1,该线程开始执行 thread th2(thread2); th1.join(); th2.join(); cout << "main..." << endl; return 0; }
aloam中互斥锁的应用
void laserCloudCornerLastHandler(const sensor_msgs::PointCloud2ConstPtr &laserCloudCornerLast2) { mBuf.lock(); cornerLastBuf.push(laserCloudCornerLast2); mBuf.unlock(); }
在接收laserCloudCornerLast2时为了完整的接收到当前帧角点点云信息在开始接受时为了防止其他线程影响该数据使用mBuf.lock();接受完后再打开mBuf.unlock();
std::lock_guard因为mutex需要在数据处理开始和结束时成对出现,当数据处理过程很长时容易遗忘造成危害,所以使用std::lock_guard
要加锁的代码段,我们用{}括起来形成一个作用域,括号的开端创建lock_guard对象,把mutex对象作为参数传入lock_guard的构造函数即可
这就相当于下面代码
std::thread thread2([&]()){ for(int i=0;i<10000;i++) { mtx.lock(); ++num; mtx.unlock(); } });
LIO-SAM中的std::lock_guard
//订阅imu里程计,由imuPreintegration积分计算得到的每时刻imu位姿 void odometryHandler(const nav_msgs::Odometry::ConstPtr& odometryMsg) { std::lock_guard<std::mutex> lock2(odoLock); odomQueue.push_back(*odometryMsg); }
在接收里程计数据时防止其他线程对接受数据造成干扰,使用std::lock_guard。该对象只在他所处的大括号内起作用
顺序容器概述
顺序容器使用原则通常,使用vector是最好的选择,除非你有很好的理由选择其他容器
Vector由于一般情况下vector使用较多,首先介绍一些vector的使用
size()返回容器中元素数目; swap()交换两个容器的内容; begin()返回一个指向容器中第一个元素的迭代器; end()返回一个表示超过容器尾的迭代器(指向容器最后一个元素后面的那个元素位置); push_back()将元素添加到矢量(vector)末尾; erase()删除矢量中给定区间的元素; insert()插入指定区间的元素到指定位置;
deque在slam接收储存一些传感器数据时也是用到队列先进先出
deque是一个的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector 容器也可以在头尾两端插入元素,但是在其头部操作效率奇差,无法被接受。
用sort对deque排序
#include <iostream> #include <deque> #include <algorithm> using namespace std; void printDeque(const deque<int> &d) { for(deque<int>::const_iterator it = d.begin(); it != d.end(); it++) { cout<<*it<<" "; } cout<<endl; } /* 回调函数 */ bool compare(int v1, int v2) { return v1 > v2; /* 此时的排序是从大到小 */ /* 如果从小到大应该改为"v1 < v2" */ } void test() { deque<int> d; d.push_back(3); d.push_back(4); d.push_back(1); d.push_back(7); d.push_back(2); sort(d.begin(), d.end(), compare); printDeque(d); }
assign()STL中不同容器之间是不能直接赋值的,assign()可以实现不同容器但相容的类型赋值,如:
list<string> names; vector<const char*> oldstyle = { "I","love","you" }; //names = oldstyle;错误!不同的类型不能执行"="操作 names.assign(oldstyle.cbegin(), oldstyle.cend()); list<string>::iterator it; for (auto it = names.begin(); names.begin() != names.end(); it++) cout << *it << " ";
在 lego-loam中有如下操作
segMsg.startRingIndex.assign(N_SCAN, 0); segMsg.endRingIndex.assign(N_SCAN, 0);
智能指针概念以及为什么引入智能指针
shared_ptr类的创建
shared_ptr<string> p1 //shared_ptr 指向string类型 shared_ptr<list<int>> p2 //shared_ptr 指向int类型的list
make_shared函数使用最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。当要用make_shared时,必须指定想要创建的对象的类型,定义方式与模板类相同。在函数名之后跟一个尖括号,在其中给出类型。例如,调用make_shared<string>时传递的参数必须与string的某个构造函数相匹配。如果不传递任何参数,对象就会进行值初始化。
//指向一个值为24的int的shared_ptr shared_ptr<int> p3 =make_shared<int>(42) //指向一个值为"99999"的string的shared_ptr shared_ptr<int> p4 =make_shared<string>(5,'9') //指向一个值为0的int的shared_ptr shared_ptr<int> p3 =make_shared<int>()
auto和make_shared通常用auto定义一个对象来保存make_shared的结果
//指向一个值为24的int的shared_ptr auto p6 =make_shared<int>(42)
shared_ptr的拷贝和赋值1、当新的 shared_ptr 对象与指针关联时,则在其构造函数中,将与此指针关联的引用计数增加1。2、当任何 shared_ptr 对象超出作用域时,则在其析构函数中,它将关联指针的引用计数减1。如果引用计数变为0,则表示没有其他 shared_ptr 对象与此内存关联,在这种情况下,它使用delete函数删除该内存。
auto p=make_shared<int>(42) //p指向的对象只有p一个引用者 auto q(p) //p和q指向相同对象,此对象有两个引用者
检查 shared_ptr 对象的引用计数
p1.use_count();
完整实例
#include <iostream> #include <memory> // 需要包含这个头文件 int main() { // 使用 make_shared 创建空对象 std::shared_ptr<int> p1 = std::make_shared<int>(); *p1 = 78; std::cout << "p1 = " << *p1 << std::endl; // 输出78 // 打印引用个数:1 std::cout << "p1 Reference count = " << p1.use_count() << std::endl; // 第2个 shared_ptr 对象指向同一个指针 std::shared_ptr<int> p2(p1); // 下面两个输出都是:2 std::cout << "p2 Reference count = " << p2.use_count() << std::endl; std::cout << "p1 Reference count = " << p1.use_count() << std::endl; // 比较智能指针,p1 等于 p2 if (p1 == p2) { std::cout << "p1 and p2 are pointing to same pointer\n"; } std::cout<<"Reset p1 "<<std::endl; // 无参数调用reset,无关联指针,引用个数为0 p1.reset(); std::cout << "p1 Reference Count = " << p1.use_count() << std::endl; // 带参数调用reset,引用个数为1 p1.reset(new int(11)); std::cout << "p1 Reference Count = " << p1.use_count() << std::endl; // 把对象重置为NULL,引用计数为0 p1 = nullptr; std::cout << "p1 Reference Count = " << p1.use_count() << std::endl; if (!p1) { std::cout << "p1 is NULL" << std::endl; // 输出 } return 0; }