什么是线程
- 在一个程序里的一个执行线路就叫做线程(thread)。线程是“一个进程内部的控制序列”。
- 一切进程都至少有一个执行线程。
- 线程在进程内部运行,本质是在进程地址空间内运行。
- 在Linux系统中,在CPU眼中看到的PCB都要比传统的进程更加轻量化。
- 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。
总结:在传统操作系统中,PCB是进程,线程有个TCB。在Linux下,因线程是通过PCB描述实现的,因此linux下PCB实际是一个线程,并且因为这些线程共用一个虚拟地址空间,因此也把linux下的线程称为轻量级进程,相较于传统PCB更加轻量化。
Linux下线程是CPU调度的基本单位,而进程是资源分配的基本单位
线程的优点
- 共用同一块虚拟地址空间,因此线程间的通信更加灵活方便。(线程间的通信方式和进程相同,还可以通过全局、传参等完成)
- 线程的创建与销毁成本比进程更低
- 线程的切换调度成本更低。
线程的缺点
- 计算密集型线程的数量比可用的处理器多,可能会有较大的性能损失。
- 因时间分配上的细微偏差或因共享了不该共享的变量而造成的不良影响的可能性是很大的,也就是说线程之间是缺乏保护的。
- 线程间缺乏访问控制,一些异常和系统调用直接对整个进程生效(exit)。
多进程稳定性、健壮性更高,适用于主程序安全性要求更高的场景
线程用途
- 针对IO密集型程序,多线程和多进程进行处理可以并行压缩等待过程
- 针对cpu密集型程序,多线程和多进程进行处理可以分摊压力
进程vs线程
- 进程是资源分配的基本单位,线程是CPU调度的基本单位
- 共享同一虚拟地址空间各线程共享进程资源和环境:
- 文件描述符表
- 每种信号的处理方式(默认、忽略、自定义)
- 当前工作目录
- 用户ID\组ID
- 但是也拥有自己的一部分独有数据:
- 线程ID
- 寄存器
- 栈
- errno
- 信号屏蔽字
- 掉的优先级
linux线程控制
POSIX线程库
- 操作系统并没有给用户直接提供一个创建好的线程接口,因此大佬们就自己封装了一套线程库<pthread.h>,用于线程控制 。
- 编译时要链接线程库"-lpthread"
线程创建
//功能:创建一个新的线程
//原型:
int pthread_create(pthread_t *thread ,const pthread_arr_t *attr ,void *(*start_routine)(void*) , void *arg);
//参数
thread:返回线程ID
atrr:设置线程属性,通常置空NULL
start_routine:是个函数地址,线程的入口函数
arg:传给线程入口的函数的参数
//返回值:成功返回0;失败返回错误码
- ps -l :查看轻量级进程信息
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void *thr_start(){
while(1){
printf ("child thread---\n");
sleep (1);
}
return;
}
int main()
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, thr_start, NULL);
if (ret != 0){
perror("pthread create error");
return -1;
}
while(1){
printf ("main thread---\n");
sleep (1);
}
return;
}
线程ID及进程地址空间布局
- pthread_create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。
- 前面说的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来表示该线程。
- pthread_create函数第一个参数指向一个虚拟地址内存单元,该内存单元的地址就是新创建线程的ID。
- pthread_t pthread_self(void)可以获得线程 自身ID。
线程退出
只终止线程而不终止进程的方法有三种:
- 从线程的入口函数运行完毕return退出。对主线程不适用,从main函数return是退出进程。
- pthread_exit终止线程本身。
功能:退出调用的线程
原型:
void pthread_exit(void *value_ptr)
参数:
value_ptr:不要指向一个局部变量
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者自身
注意:return或者pthread_exit返回的指针所指向的内存单元必须是全局或malloc分配的
- pthread_cancel终止同一进程的其他线程。
功能:取消一个执行中的进程
原型:
int pthread_cancel(pthread_t thread);
参数:
thread:线程ID
返回值:成功返回0;失败返回错误码
线程等待
需要线程等待的原因:
- 线程默认情况下是joinable属性,表示线程退出不会自动释放资源
- 线程退出后不会完全释放资源,仍然在进程的地址空间内,需要被其他进程等待,等待这个线程退出,获取这个退出线程的返回值并回收资源。
- 创建新的线程不会复用刚才退出线程的地址空间。
功能:等待线程结束
原型:
int pthread_join(pthread_t thread ,void **value_ptr);
参数:
thread:线程ID
value_ptr:它也是一个指针,是指向一个指向线程返回值的指针
返回值:成功返回0;失败返回错误码
线程分离
- 分离线程就是将线程从joinable属性设置为detach,表示分离线程
- 被分离并处于detach属性的线程推出后,则会自动释放所有资源。
int pthread_detach(pthread_t thread)
//可以是线程组内其他线程对目标线程进行分离,也可以是线程自己进行分离
pthread_detach(pthread_self());
注意:joinable和分离是冲突的,一个线程不可以即是joinable又是分离的。