多线程
多线程程序中需要保证数据的同步,否则可能出现数据不一致的情况,导致程序逻辑错误、混乱,乃至数据引用不一致,导致内存溢出、数据验证出现错误,程序崩溃。
因此在多线程中会使用锁,加锁的目的是保证共享资源在任意时间里,只有一个线程访问,这样就可以避免多线程导致共享数据错乱的问题。
某个线程在拿到锁后,其他线程因为无法拿到锁而阻塞或运行失败(在SLAM中一般为阻塞),从而保证不同线程所引用数据的一致性,防止内存访问、申请出错。
举例
例如在未使用锁的情况下遍历处理一组vector数据,在for循环中使用了vector.size()作为循环停止条件,但在程序遍历过程中,vector中部分元素被其他线程删除,该部分内存被分配为其他类型数据,则产生内存访问错误问题。
ORB-SLAM多线程
具体而言,ORB-SLAM的跟踪(Track)、局部优化(localMapping)、回环检测(loopClosing)以及回环检测成功后的全局优化(GlobalMapping)、可视化(Viewer)均为单独的线程,将任务拆分,从而保证程序能够实时运行。
在线程中访问如下数据,如Map中的mspMapPoints、mspKeyFrames、mvpReferenceMapPoints,MapPoint中的位置、特征,Frame中的位置姿态、特征、连接关系时需要加锁,因为这些数据可能会被多个线程同时访问。
当然,在SLAM中也是有一些数据在同一时间只会被一个线程访问,或者在整个SLAM过程中只会被一个线程访问,例如在跟踪(Track)线程中设置当前的参考关键帧(即在当前local MapPoints中共视点最多的关键帧)或设置local MapPoints中MapPoints最新被帧观测到的帧ID,不需要加锁。
举例
在某个线程中进行数据修改时,目前我想到有如下两种方法,使用场合主要取决于数据量的大小。
如果数据量比较小,则直接在锁住后修改,例如:
void MapPoint::SetWorldPos(const Eigen::Vector3f &Pos) {
unique_lock<mutex> lock2(mGlobalMutex);
unique_lock<mutex> lock(mMutexPos);
mWorldPos = Pos;
}
Eigen::Vector3f MapPoint::GetWorldPos() {
unique_lock<mutex> lock(mMutexPos);
return mWorldPos;
}
如果数据量比较大,即为容器类数据,则在锁住后通过插入、删除修改,例如:
void LocalMapping::InsertKeyFrame(KeyFrame *pKF)
{
unique_lock<mutex> lock(mMutexNewKFs);
mlNewKeyFrames.push_back(pKF);
mbAbortBA=true;
}