线程
什么是线程
在一个程序里的一个执行路线就叫做线程。线程是一个进程内部的控制序列。
一个进程至少有一个线程。
进程与线程(进程与线程总结)
进程的多线程共享
- 地址空间
- 文件描述符
- 信号的处理方式
- 当前工作目录
- 用户ID和组ID
线程优点:
- 创建一个新线程的代价要比创建一个新进程小得多。
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。
- 线程占用的资源要比进程少很多。
- 计算密集型应用,为了能在多处理器系统上运⾏行,将计算分解到多个线程中实现。
- I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
线程缺点:
- 性能损失:不能共享同一个处理器。如
果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失。
。 2. 健壮性降低:线程间的共享导致,线程之间缺乏保护。 - 缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
- 编程、调试难度高。
线程控制
POSIX线程库
- 与线程有关的函数构成了一个完整的系列,绝⼤大多数函数的名字都是以“pthread_”开头。
- 头文件
pthread.h
- 链接这些线程函数库时要使用编译器命令的“-lpthread”选项。
创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*star
t_routine)(void*), void *arg);
thread:返回线程ID。
attr:设置线程的属性,NULL表示默认属性。
start_routine:函数地址,线程启动后执行的函数。
arg:传给线程启动函数的参数
返回值:成功返回0,失败返回错误码。
线程ID和进程ID
- 在Linux中,线程被称为轻量级进程(Light Weighted Process),每一个用户态的线程,在内核中都对应一个调度实体,也拥有自己的进程描述符(task_struct结构体)。
- 没有线程之前,一个进程对应内核⾥里的一个进程描述符,对应一个进程ID。但是引入线程概念之后,情况发生了变化,一个用户进程下管辖N个用户态线程,每个线程作为一个独立的调度实体在内核态都有自己的进程描述符,进程和内核的描述符一下子就变成了1:N关系。
- 所以,Linux内核引入了线程组的概念。
多线程的进程,又被称为线程组,线程组内的每一个线程在内核之中都存在一个进程描述符
(task_struct)与之对应。进程描述符结构体中的pid,表面上看对应的是进程ID,其实不然,它
对应的是线程ID;进程描述符中的tgid,含义是Thread Group ID,该值对应的是用户层面的进程ID。
用户态 | 系统调用 | 内核进程描述符中对应的结构 |
---|---|---|
线程ID | pid_t getpid(void) | pid_t pid |
进程ID | pid_t getpid(void) | pid_t tgid |
- 内核在创建第一个线程时,将线程组的ID设为第一个线程的线程ID。所以线程组内存在一个线程ID等于进程ID,该线程为线程组的主线程。
- 至于线程组其他线程的ID则有内核负责分配,其线程组ID总是和主线程的线程组ID一致,无论是主线程直接创建线程,还是创建出来的线程再次创建线程,都是这样。
- 线程和进程不一样,进程有父进程,但是线程都是对等关系。
线程终止
- 从线程函数return。主线程不适用。
- 调用pthread_exit(无返回值)终止自己。
- 调用pthread_cancel(成功返回0,错误返回错误码)终止统一进程中的另一个线程。
线程等待和分离
为什么要线程等待?
已经退出的线程,空间不会被释放,创建的新线程无法使用退出线程的地址空间。
线程等待
int pthread_join(pthread_t thread, void **value_ptr);
thread:线程ID
value_ptr:指向一个指针,后者指向线程返回值
返回值:成功返回0,失败返回错误码
调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的。
- return返回:value_ptr所指向的单元存放thread线程函数的返回值
- 被pfhread_cancel异常终止:value_ptr所指向的单元存放常数PTHRAD_CANCELED
- pthread_exit自行终止:value_ptr所指向的单元存放传给pthread_exit的参数
- value_ptr也可以为NULL
线程分离
- 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法
释放资源,从而造成内存泄漏。
- 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
int pthread_detach(pthread_t thread);
可以是线程组内其他线程对目标线程进行分离
pthread_detach(pthread_self());
也可以是线程自己分离
jionable和分离是冲突的,一个线程不能既是joinable又是分离的
线程同步与互斥
同步,又称直接制约关系,是指多个线程(或进程)为了合作完成任务,必须严格按照规定的 某种先后次序来运行。
互斥,又称间接制约关系,是指系统中的某些共享资源,一次只允许一个线程访问。当一个线程正在访问该临界资源时,其它线程必须等待。
- 互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
- 同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。
- 同步其实已经实现了互斥,所以同步是一种更为复杂的互斥。
- 互斥是一种特殊的同步。