最近,一位学长面试阿里,不幸止步二面(再次,为这位大牛深感遗憾)。面试中涉及到对多线程编程的理解,并问道如果许多并发的线程中,有一个崩溃了,你如何检查出是哪一个?下面结合C++11对多线程的支持学习一下多线程编程。
进程和线程都是操作系统层面的概念,进程是资源分配的基本单位,而线程则是cpu调度的基本单位。在进程提出时,是没有线程的概念的。由于操作系统是多任务并发执行的,但对进程进行调度时,需要对进程占用的资源进行回收和分配,然而有些调度实际上并不涉及资源归属的变更,为了减少资源回收和分配引起的不必要的开销,在进程之上提出了线程的概念。一个进程下可能包含多个线程,这些线程共同拥有它们所属进程的资源。这样一来调度的对象从进程变为线程,调度的过程中不涉及资源的回收和分配,从而提高了系统的效率。
既然线程是操作系统层面的,那么对线程的管理和操作就依赖于不同操作系统提供的接口。但是许多高级语言为多线程编程提供了统一的接口,屏蔽了操作系统级别的操作,消除了不同平台的差异性,提高了代码的可移植性。在这方面,java无疑是杰出的代表。不过,生命力旺盛的C++在新标准11推出后,也提供了良好的对多线程的支持。与之相关的头文件主要有:<thread>、<mutex>、<atomic>、<future>等。
一个最基本的启动线程的例子:
#include <thread>
#include <iostream>
using namespace std;
void fcn(int a,int b){
cout<<a<<endl;
std::this_thread::sleep_for(chrono::seconds(5)); //this_thread是包含于标准库命名空间下的与多线程有关的命名空间,chrono是新标准中提供的操作时间的命名空间
cout<<b<<endl;
}
int main(){
thread t1(fcn,3, 4); //开启一个线程,可以传递参数
t1.join(); //阻塞主进程(main)直到相应的子进程结束
cout<<"main"<<endl;//重新唤起主进程
system("pause");
}
互斥锁包含在头文件mutex中,新标准提供了如下四种mutex:std::mutex,std::recursive_mutex,std::timed_mutex, std::recursive_timed_mutex。
std::mutex只能lock一次,而std::recursive则支持lock多次,然后unlock相应次数后才解锁,相当于提供了一个计数器。后两个则在前两个的基础上加了时间控制,提供 try_lock_for/ try_lock_until方法,允许你等待一个lock,直到超时,或者达到定义的时间。
关于多线程的调试
gdb是linux下的使用最广泛的多线程调试工具。windows下,VS也提供对多线程的调试,按F10逐过程运行,在“线程”窗口中会显示:线程ID,进程ID,线程名称,类别,等信息。