- 我最近开了几个专栏,诚信互三!
====> |||《算法专栏》::刷题教程来自网站《代码随想录》。|||
====> |||《C++专栏》::记录我学习C++的经历,看完你一定会有收获。|||
====> |||《Linux专栏》::记录我学习Linux的经历,看完你一定会有收获。|||
====> |||《C#专栏》::记录我复习C#的经历,深度理解,查漏补缺,不定期更新。|||
====> |||《计算机网络专栏》::记录我学习计算机网络,看完你一定会有收获。|||
以Linux为例了解线程
- 什么是线程
- 为什么要有线程
- Linux下的“线程”
- 线程结构
- 线程的优缺点
- 线程创建
- 线程退出
- 线程等待
什么是线程
线程是进程的一个执行分枝,线程是CPU调度与执行的基本单位。
而我们之前学过的进程,则是OS资源分配的基本单位。
进程与线程之间的关系如下图。
在一个进程内,每个线程的大多数资源都是共享的,但是也又一些资源并非共享,我们下面会介绍。
为什么要有线程
CPU要完成并发,我们之前是使用多进程,多进程需要开辟多个进程地址空间,这样对内存的开销很大,并且多进程对cache的利用率不高,导致多进程的调度效率不如多线程。
Linux下的“线程”
在Linux操作系统下,是没有线程概念的,但存在一个轻量级进程(LWP)的概念,而实现Linux下的线程则是在Linux原生库中,并且分装了LWP,所以Linux中线程也被称为用户级线程。
并且Linux下的进程也是分装了LWP的,所以Linux下进程和线程的根源都是LWP。
线程结构
由于Linux下的线程不属于系统库,所以操作系统不会对线程进行管理,实际上,Linux下对于线程的管理是由原生线程库完成的。
以下为线程结构体结构。
1).每个线程都拥有单独的栈空间,主线程的栈空间就是地址空间中的栈,而其他线程的栈空间则是自带的。
2).线程还存在一个线程局部存储区域,该区域可以将多线程的共享资源,拷贝一份到线程局部存储区域内,(__thread)作为线程的单独资源来使用,只能拷贝C语言的内置类型。
3).属性信息则是记录线程的属性,比如该线程是否分离等等。
在Linux中使用线程时,我们一定要链接原生线程库,此时每个进程创建的线程都会被组织在库中,如下图。
可以看到,在线程库内,线程类似与数组一样被组织起来。
线程的优缺点
现在我们可以总结一下线程的优缺点,我们类比进程。
优点:
1).用多线程实现并发比多进程要更加节省空间。
2).多线程CPU的调度速度,比多进程调度速度更快。
3).多线程对计算密集型和IO密集型的程序效率更高。
缺点:
1).线程没有访问控制,多线程中,先运行那个线程,是不确定的。
2).大量线程会导致CPU运行效率降低。
3).多线程代码会使得代码健壮性降低。
多线程的绝大部分资源都是共享的,但是也有部分资源不共享。
1).每个线程都有自己的栈空间。
2).线程是CPU调度与执行的基本单位,所以每个线程都有自己的一组寄存器。
同时也有很多资源是共享的,其中比较容易记混的。
1).所以线程都共有一个文件描述数组,及文件描述数组是进程独有的资源。
2).地址空间都是线程共有的。
线程创建
线程创建的接口如下
pthread_create
参数1:thread是一个线程的id,用于在用户层标示一个唯一的线程,它是一个输出型参数,只需要传入,由线程创建接口返回该值。
参数2:是创建线程时可以赋予其属性,传入NULL即可。
参数3:是线程对应执行的方法,函数指针类型。
参数4:是线程执行方法的参数。
返回值代表成功与否。
要注意的是,创建出的线程函数的参数和返回值都是void * 类型, 这是因为void类型无法定义变量,而void * 类型任何类型的指针都可以接收和返回。
线程的tid实际上,是一个虚拟地址。
Linux中线程的维护是通过线程库进行的,线程库是动态库,线程要加载动态库到进程地址空间上,这样线程库内的所有资源都有了虚拟地址,每个线程的tid其实就是tcb的虚拟地址。
线程退出
在新线程内,可以使用return 退出新线程,但是对于主线程,它和进程捆绑,所以主线程return 就代表进程退出,而原生线程库提供了一些线程退出的接口。
pthread_exit
参数1:retval代表新线程要返回的值,该接口可以代替return。
我们也可以在其他线程内让某个线程退出。
pthread_cancel
参数1:接收一个线程的tid,将线程取消,要注意,该接口不止能在主线程内取消其他线程,在其他线程内,乃至自己本身都能取消自己。
同时也可以自己退出自己
但我们还需要使用一个接口获得自己的tid。
pthread_self
直接使用,获得当前线程的tid。
线程等待
如同进程一样,主线程也需要阻塞等待新线程,同时回收新线程的资源。
pthread_join
参数1:代表主线程要等待那个线程。
参数2:是一个输出型参数,接收新线程的返回值。
主线程对其他线程需要进行阻塞等待,如果对线程的返回值无要求,那么我们可以将某个线程设置为分离,这样在该线程退出时,主线程就无需阻塞等待回收结果了。
pthread_detach
参数1:某个线程的tid,对某个线程进行分离,可以在线程内对自己分离,但是建议在主线程内对某些线程进行分离。
同时我们被分离的线程,我们不用对线程进行等待,也不能对线程进行等待。