多线程例子:
multhread.cpp

#include <thread>
#include <iostream>

using namespace std;

void myfunc(){
        cout << "sub thread is starting" << endl;
        cout << "sub thread id:" << this_thread::get_id() << endl;
        cout << "sub thread is ending" << endl;
}

int main(){
        cout << "Main thread is starting" << endl;
        cout << "Main thread:" << this_thread::get_id() << endl;
        thread mythread(myfunc);
        mythread.join(); // 等待子线程执行完成,否则可能会出现主线程执行完,强制结束子线程的执行
        cout << "Main thread is ending" << endl;
        return 0;
}

编译并运行:

$ g++ multhread.cpp -o multhread
$ ./multhread
Main thread is starting
Main thread:0x1df2c5000
sub thread is starting
sub thread id:0x16bd07000
sub thread is ending
Main thread is ending

C++的多线程编程其实自C++11之后就变得比较简单起来,为什么这么说呢?我们先说C++11 C++14 C++17等是什么意思?
其实自C++98就是指1998年发布以来就没有什么大的版本发布,但是这期间积累了很多的改进,尤其是C++得到更广泛的应用之后,于是在2011年就发布了C++11,这个版本增加了很多编程实践和新的改进,后面在C++14(2014),C++17(2017)都是相对C++11(2011)做了一些补充和优化。

所以我们在谈论C++多线程编程的改进都是C++11提供的。每个进程都会有一个主线程,这个主线程是唯一的,也就是说进程中只能有一个主线程,否则这个程序别想运行了。因为在现代的操作系统中,一个运行的程序,首先是创建了一个进程,这个进程就是为执行程序代码而创建的,它会获得系统的分配的资源,并且受到系统的控制和调度。线程是真正利用资源执行程序代码的实体。因此一个程序运行后,必须有一个主线程。

打个比方,进程就像一个公司,成立一个公司就是创建一个进程,这个公司会有办公空间、水、电、消防等资源,好比进程分配得到了内存、文件访问、占用某个物理设备(如打印机等),这个公司至少一个人(法人)来运作。好比进程里的主线程来负责执行程序。

除了主线程外,还可以创建更多的线程来执行其他任务,就像一个公司可以招更多的人进来,每个人都相当于一个子线程。

在现代程序中,一个程序启动后,随之就会创建一个进程,主线程也会跟着启动起来。主线程是从main函数开始执行的,创建的子线程也是从某个函数开始执行的,主线程会在main主函数执行完后即出,子线程也是如此,从某个函数开始执行,一旦这个函数执行完毕,这个子线程也就运行结束。另外,要明白一样的东西:程序是否执行结束的标志是主线程是否执行完毕,当主线程执行完毕了,整个程序就执行完毕了,此时,如果还有其他子线程还没有执行完毕,也会被操作系统强制终止。所以如果想让子线程一直处于运行状态,前提就是必须保证主线程也一直是保持运行的状态,千万不能让主线程运行完毕了。

一个程序的子线程,并不是越多越好,因为每个线程都需要有一个独立的堆栈空间(这个耗费内存),线程间的切换需要保存许多中间状态等(即上下文切换,若线程太多,上下文切换就会很频繁,上下文切换是必须的,但是这种切换如果是没有意义的额外工作,那么只会浪费本属于程序运行的时间)

为什么说C++11的多线程编程会比较简单呢?因为它提供了新的线程库,写C++程序,一般都会考虑平台移植的问题,即系即想程序可以在windows平台上编译成功,也想在linux、macos平台上成功编译。但是多线程是涉及到CPU的使用问题,每个平台的处理和提供出来的系统调用都不相同,如在windows平台,要用CreateThread函数创建线程;在Linux平台,要用pthread_create创建线程,代码不一样,在跨平台使用时,就会比较麻烦。为了解决这些问题,曾经出现过一些跨平台的多线程库如POSIX thread(pthread)。这个多线程库在linux、macos、windows上使用时,都要配置一些东西,才能够使用。总的来说还是不够方便。

自C++11开始,增加了对多线程的支持,我们可以使用C++语言本身提供的编程接口来编写与操作系统平台无关的多线程程序。极大增加了程序的可移植性,用这个多线程接口来编写的多线程程序可以在各个平台上成功编译。

要使用C++11提供的多线程支持,只需要加入以下头文件:

#include <thread>

创建一个线程,thread这个类就是用来创建新线程的:

std::thread mythread(myfunc);

join函数的用途:
主线程执行到下面这行就阻塞在那,直到子线程执行完毕。

mythread.join();

在编程的实践中,一般来说,主线程必须等待子线程结束才结束,所以上面的程序,如果不调用join等待子线程执行完毕,主线程就会先结束,主线程先结束了,还没有执行完的子线程就会被操作系统强制终止。随着编程的实践,也出了这样一种需求,主线程不必等待了线程执行结束,可以先结束,但又不影响子线程的继续执行,所以C++引入了detach,即:

int main(){
		...
        mythread.detach(); // 子线程的执行与主线程的执行进行分离
        ...
}

这样主线程结束了,子线程仍然可以转入后台继续运行。守护线程就是这样做出来的。 一般来说,子线程的输出也不会看到,因为输出结束的窗口是与主线程关联的,现在主线程结束了,子线程转入后台运行,输出的结束就很难被看见。

在C++项目中,join的使用比detach要多,使用detach会导致我们失去对线程的控制。下次我们来聊一聊用lamda表达式创建子线程。