C++多线程编程

多线程是学习SLAM过程中不可缺少的一步,正确的使用多线程能使SLAM系统的运行速度提升很多,达到更高的精度和速度。本节就来学习SLAM中的多线程编程方法。

线程

概念

线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。

优点

线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,那么通信就需要在用户空间和内核空间进行频繁的切换,开销很大。但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。

C++

一个简单的Demo

#include <iostream>
#include <thread>
#include <future>
using namespace std;
void helloworld()
{
cout << "hello world \n";
}
int main()
{
//开启一个线程
std::thread t(helloworld);
std::cout << "hello world main thread\n";

//线程的终结
t.join();

return 0;
}

多线程库

C++11中终于提供了多线程的标准库,提供了线程管理、保护共享数据、线程间同步操作、原子操作等类。

多线程库对应的头文件是#include<thread>,类名为std::thread

一个简单的串行程序如下:

#include <iostream>
#include <thread>

void function_1() {
std::cout << "I'm function_1()" << std::endl;
}

int main() {
function_1();
return 0;
}

这是一个典型的单线程的单进程程序,任何程序都是一个进程,main()函数就是其中的主线程,单个线程都是顺序执行。

将上面的程序改造成多线程程序其实很简单,让function_1()函数在另外的线程中执行:

#include <iostream>
#include <thread>

void function_1() {
std::cout << "I'm function_1()" << std::endl;
}

int main() {
std::thread t1(function_1);
// do other things
t1.join();
return 0;
}

分析:

  1. 首先,构建一个std::thread对象t1,构造的时候传递了一个参数,这个参数是一个函数,这个函数就是这个线程的入口函数,函数执行完了,整个线程也就执行完了。
  2. 线程创建成功后,就会立即启动,并没有一个类似start的函数来显式的启动线程。
  3. 一旦线程开始运行, 就需要显式的决定是要等待它完成(join),或者分离它让它自行运行(detach)。注意:只需要在std::thread对象被销毁之前做出这个决定。这个例子中,对象t1是栈上变量,在main函数执行结束后就会被销毁,所以需要在main函数结束之前做决定。
  4. 这个例子中选择了使用t1.join(),主线程会一直阻塞着,直到子线程完成,join()函数的另一个任务是回收该线程中使用的资源。

线程对象和对象内部管理的线程的生命周期并不一样,如果线程执行的快,可能内部的线程已经结束了,但是线程对象还活着,也有可能线程对象已经被析构了,内部的线程还在运行。

线程创建

下面的程序,我们可以用它来创建一个 POSIX 线程:

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)

在这里,pthread_create 创建一个新的线程,并让它可执行。下面是关于参数的说明:

参数 描述
thread 指向线程标识符指针。
attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine 线程运行函数起始地址,一旦线程被创建就会执行。
arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。

终止线程

使用下面的程序,我们可以用它来终止一个 POSIX 线程:

#include <pthread.h>
pthread_exit (status)

在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

打赏
posted @ 2020-05-15 07:27  少年笔谈  阅读(225)  评论(0编辑  收藏  举报