线程概念:
通常来说,线程是一个程序的一条执行流,更准确来讲是一个进程内部的控制序列。在Linux下线程是用PCB模拟的,线程也被称作轻量级进程,因此Linux下的线程是CPU调度的基本单位(与传统系统相比Linux下没有真正的线程)。由于线程是在进程内部运行的执行流,所有线程共享进程资源。
线程优缺点:
优点:
1. 创建一个线程代价要比创建一个进程小的多;
2. 与进程之间的切换相比,线程间的切换需要系统做的工作比较少;
3. 线程间通信比较方便;
4. 线程占用资源比进程少很多;
5. 在等待较慢的IO操作时,程序可以执行其他任务;
6. 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多线程中计算;(分摊计算)
7. IO密集型应用,为了提高性能将IO操作重叠,线程可同时等待不同的IO操作。 (分摊等待)
缺点:
1. 增加了调度的开销和成本,消耗过多的CPU,造成性能损失;
2. 线程间共用同一块地址空间,所以每个线程会受限于地址空间大小;
3. 健壮性降低;
4. 缺乏访问控制;
5. 多线程对临界资源进行操作容易造成资源混乱;
6. 如果一个线程异常,将会造成所有线程异常。
延伸:1. 比较进程与线程:
进程是分配系统资源的基本实体;
线程是CPU调度的基本单位;
我们知道进程的独立性:每个进程都有自己的虚拟地址空间,页表,映射关系,物理内存,数据。同样的线程也有自己的栈角和数据(线程id,一组寄存器,信号屏蔽字,调度优先级,error)。
2. 线程间共享那些资源?
- 文件描述符表
- 信号处理方式
- 工作目录
- 用户ID和组ID
3. 线程实现方式:
内核支持线程 KST
优点:
- 多线程可以并行处理;
- 如果进程中一个线程阻塞,其他进程依然可以运行;
- 内核支持线程具有很小的数据结构和栈,切换比较快,开销小;
- 内核本身为多线程,可以提高系统执行速度和效率。
缺点:
与用户级线程相比,其模式切换开销较大(因为需要从用户态切换到内核态),对系统消耗大。
- 用户级线程 ULT
优点:
- 线程切换不需要转换到内核空间,只在用户态完成;
- 调度算法可以是进程专用的;
- 用户级线程的实现与操作系统平台无关。
缺点:
- 线程阻塞问题;
- 单纯的用户级线程实现方式中,多线程应用不能利用多处理机进行多重处理的优点,即系统只为进程分配一个CPU,每次仅有一个线程能执行,其他只能等待。
- 组合方式:
1. 多对一,将用户线程映射到一个内核线程。
优点:线程管理开销小,效率高。
缺点:线程阻塞问题;多个线程不能在多处理机上执行。
2. 一对一,将每个用户线程对应一个内核线程。
优点:没有线程阻塞问题;多线程可以在多处理机上执行。
缺点:占系统内核开销大,所以对线程数量要加以限制。
3. 多对多,许多用户进程映射少量内核线程。
兼容上述优点,消除上述缺点。
线程控制:
操作系统并没有提供相应实现线程控制的系统调用接口,但有为线程控制封装的线程库。使用这套接口创建的线程称为用户态线程,它在操作系统内部提供了一个轻量级进程。
线程创建:
/*
通过参数返回一个用户态的线程id(是线程地址空间在进程虚拟地址空间中的首地址),
用户态线程的操作都是围绕这个用户态的线程id来操作的。
*/
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
/*
thread // 返回线程ID
attr // 设置线程的属性,attr为NULL表⽰使⽤默认属性
start_routine // 是个函数地址,线程启动后要执⾏的函数
arg // 传给线程启动函数的参数
返回值: 成功返回 0; 失败返回错误码
*/
线程等待:
作用:主线程获取新线程的退出状态,避免产生僵尸进程。
属性:
- 只能等待指定的线程;
- 只有线程处于joinable状态(线程默认属性),这个线程才能被等待。
- 线程分离:线程退出后,直接释放资源,无法获取返回值。
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
/*
thread // 线程ID
value_ptr // 它指向⼀个指针,后者指向线程的返回值
返回值: 成功返回0 失败返回错误码
*/
线程分离:
由于新线程创建出来后,主线程必须进行等待,线程等待是阻塞的,此时主线程什么都不能做。线程分离,将新线程分离出去,当新线程结束后自动退出,释放资源,主线程不需要再关心新线程退出。
- 可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离。
- joinable和分离是冲突的,一旦线程被分离将不能再被等待。
#include <pthread.h>
int pthread_detach(pthread_t thread);
线程终止:
- 从线程函数return(),线程就退出了。
- 主线程就是从main()函数退出,主线程退出即进程结束。
- 主线程要等待其他线程退出(线程等待),否则会出现类似僵尸进程的情况,出现内存泄漏。
/*
线程退出 ---> 主动退出(谁调用谁退出)
*/
#include <pthread.h>
void pthread_exit(void *retval);
线程取消:
取消执行中的一个线程。
/*
线程取消 ---> 被动退出(退出被调用的线程)
*/
#include <pthread.h>
int pthread_cancel(pthread_t thread);