进程和线程
相同点:都是参与时间片轮转的任务,都有5个状态
不同点:
-
线程不能独立存在,只能隶属于创建它的进程
进程实际上是线程的容器,任何进程至少包含一个称为主线程的线程,执行main函数所在的执行序列,这个线程是系统为进程自动创建
而其余线程则进程通过调用相关函数来创建
-
进程是操作系统分配资源的基本单位
每个进程有用自己独立内存四区和描述符数组
每个线程只有自己独立的栈区,其它三区和描述符数组共用其隶属的进程的
-
线程是参与时间片轮转的最小单位
进程(借助于主线程)和线程都会参与时间片轮转,二者的体量不同而已,进程的体量比线程的体量更大,因此,线程被称为轻量级任务,进程被称为重量级任务
-
多进程比多线程更为安全
一个进程崩溃了,不会导致其它进程的崩溃
一个线程崩溃,会导致其隶属的整个进程的崩溃,进程中的其它线程也就无法继续运行
-
线程间通信比进程间通信更为便利
同属一个进程的多线程间,可以很方便地通过同一块内存空间来进行通信
进程间只能通过操作系统提供相关机制进行通信
线程(thread)是参与CPU时间片轮转的最小单位,它只能从属于创建它的进程,其拥有独立的栈区,但数据区、代码区、堆区、描述符数组共用其隶属进程的,多线程(均隶属于同一个进程)间通信非常便利,但一个线程崩溃会导致整个进程崩溃
Linux操作系统本身的系统函数来实现多线程非常不方便,因此GNU组织对与线程相关的系统函数进行二次封装,形成一个线程库,应用程序编程只需调用线程库提供的接口就行!
线程库提供了一个头文件:pthread.h,所有应用程序编程所需的接口声明都在这个头文件里,因此,应用程序都应先包含该头文件然后使用接口,而编译程序也需为gcc加上选项:-lpthread 用来告诉gcc链接时需要使用线程库
线程库提供的相关函数均是以pthread_开头
p-posix标准:是一个国际标准化组织,为不同操作系统制定的一套与线程相关的统一的接口标准
一、线程管理
每个线程都有一个身份标识,被称为线程id,Linux下使用pthread_t类型表示线程id的数据类型
一个线程想要获得自己的id,可调用如下函数:
pthread_t pthread_self()
Linux下线程id本质是整数,但其它操作系统未必是整数,因此需要比较线程id时建议使用线程库提供的相关函数进行比较.例如:
int pthread_equal(pthread_t id1,pthread_t id2);
对于线程id,只需保证同一个进程中不同线程具有唯一的id即可,无需整个系统唯一
查看线程相关的命令
ps -efL:显示所有进程的所有线程
ps -f -L -p pid:显示指定进程的线程
ps -fL 进程号:查看指定进程的所有线程状态
二、线程创建
三、线程退出
线程正常退出方式:
- 入口函数返回
- 执行到pthread_exit函数调用语句 ----- 让一个线程中途正常退出
线程异常退出方式:
- 接收并响应取消请求
实际项目开发过程中建议采用入口函数返回方式让线程退出
思考:在主线程调用pthread_exit会如何?在新线程调用exit会如何?
一个线程退出,不会导致同一进程里其它线程退出,也就是说一个线程退出后进程仍然存在(最后一个线程退出除外)
一个进程退出,其内所有线程都将无法继续执行,也即导致所有线程异常退出
因此,主线程中调用pthread_exit只是让主线程停止执行,其余线程可以继续执行
任何一个线程里调用exit都会导致整个进程的退出,从而导致所有线程退出
四、线程善后
一个线程退出后成为僵死态线程(简称为僵尸线程),为了及时释放已处于僵死态线程仍然占用的资源需要对齐及时进行善后处理
线程的善后处理有两种方式:
- 对指定线程调用pthread_join函数
- 让线程成为分离的线程
根据善后处理方式,线程被分为两种:
1. 可联结线程:可以调用pthread_join函数进行善后的线程
2. 分离的线程:不可以调用pthread_join函数进行善后的线程,分离线程的善后由系统自动进行
五、线程分离
让一个线程成为分离的线程有两种方式:
-
对指定的线程调用pthread_detach函数
一个线程也可以调用pthread_detach让自己成为分离的线程:
pthread_detach(pthread_self())
- 在创建线程时通过线程属性让新线程一开始就是分离的线程
pthread_attr_t attr;//pthread_attr_t是一个结构体类型的别名
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);`
pthread_create(&tid,&attr,入口函数名,传给入口函数的实参);
pthread_attr_destroy(&attr);
int pthread_attr_init(pthread_attr_t *pattr)
函数功能:初始化pattr指向的pthread_attr_t类型的元素
参数作用:
pattr:指示被初始化的pthread_attr_t类型元素的地址
返回值:成功0,失败错误编号
void pthread_attr_destroy(pthread_attr_t *pattr)
函数功能:清理pattr指向的pthread_attr_t类型的元素
参数作用:
pattr:指示被清理的pthread_attr_t类型元素的地址
int pthread_attr_getdetachstate(const pthread_attr_t *pattr, int * detachstate)
函数功能:从pattr指向的pthread_attr_t类型元素里获取是否分离的状态
参数作用:
pattr:指示被获取的pthread_attr_t类型元素的地址
detachstate:结果参数,存放获取到的结果
返回值:成功返回0,失败返回错误编号
int pthread_attr_setdetachstate(pthread_attr_t * pAttr, int detachstate)
函数功能:向pattr指向的pthread_attr_t类型元素设置是否分离的状态
参数作用:
pattr:指示被获取的pthread_attr_t类型元素的地址
detachstate:设置的分离状态值
返回值:成功返回0,失败返回错误编号
线程库定义两个宏对应分离状态值:
PTHREAD_CREATE_DETACHED:用此属性创建的线程是分离的
PTHREAD_CREATE_JOINABLE:用此属性创建的线程是可联结的
六、线程取消 ---- 了解
线程库中提供了一个如下函数:
int pthread_cancel(pthread_t tid)
函数功能:给指定线程发送取消请求
一个线程要不要响应取消请求、以什么方式响应取消请求都可以通过专门的函数进行设置
有一种响应取消请求方式是:
线程收到取消请求不会立即退出,而是继续执行到称为取消请求点的代码位置才会退出
操作系统规定一些系统函数作为线程的取消请求点,Linux下可以用命令:
man pthreads 查阅这些函数