
第一部分:线程分离属性
- 分离属性概念
线程结束后,系统自动回收线程空间,无需手动调用 pthread_join。
- 相关操作函数
· 属性初始化:对线程属性对象进行初始化,为后续设置做准备。
· 属性销毁:销毁已初始化的线程属性对象,释放相关资源。
· 设置分离状态:将线程属性设置为分离状态,线程结束后自动回收。
第二部分:多进程与多线程对比
- 运行效率
多线程优于多进程。同一进程内的线程间任务调度效率更高,切换开销更小。
- 安全性
多进程优于多线程。进程空间相互独立,一个进程异常结束不会影响其他进程;线程位于进程内部,一个线程异常结束会导致整个进程终止,其余线程随之结束。
- 通信实现便利性
多线程优于多进程。线程共享进程的文本段、数据段和堆区,可通过共享内存直接通信;进程空间独立,没有共享区域,无法直接通信。
- 通信编程复杂度
多进程编程复杂度更高。多线程使用全局变量通信会引入资源竞争问题,需加锁保护;多进程空间独立,需借助第三方机制完成通信。
第三部分:线程间通信基础概念
- 原子操作
不能被CPU任务调度打断的一次最小操作。
- 临界区(临界代码)
加锁和解锁之间的代码区域。该区域内的代码在同一时刻最多只能被一个线程执行,从而保证数据的一致性。
第四部分:互斥锁
- 互斥锁的作用
解决多线程使用共享资源时的竞争问题,保证同一时刻只有一个线程进入临界区。
- 核心函数接口
· 初始化:对互斥锁进行初始化,可指定属性(默认传NULL)。
· 销毁:销毁已初始化的互斥锁,释放相关资源。
· 上锁:申请锁资源。若锁已被其他线程占用,则阻塞等待直到锁可用。
· 解锁:释放锁资源,唤醒等待该锁的其他线程。
- 使用模式
标准使用流程为:初始化 → 上锁 → 访问临界资源 → 解锁 → 销毁。
第五部分:死锁
- 死锁概念
多个线程因互相持有对方需要的锁资源而无法继续执行,陷入永久等待的状态。
- 死锁产生的四个必要条件
· 互斥条件:资源一次只能被一个线程占用。
· 不可剥夺条件:线程已获得的资源在使用完毕前不能被强制剥夺。
· 请求与保持:线程在持有部分资源的同时请求其他资源。
· 循环等待:多个线程形成闭环等待关系,每个线程都在等待下一个线程释放资源。
- 避免死锁的方法
· 使用非阻塞上锁函数替代阻塞上锁函数,尝试获取锁失败后可释放已持有的锁并重试。
· 多把锁的场景下,所有线程保持一致的加锁顺序,避免循环等待。
第六部分:信号量
- 信号量概念
信号量是一种资源计数器,用于控制对共享资源的访问数量。代码、变量、CPU、内存等都可视为资源。
- 信号量特性
资源一定是有限的,信号量通过计数来反映可用资源数量。
- 核心函数接口
· 初始化:设置信号量的初始值,并指定共享范围(同一进程内线程间共享或跨进程共享)。
· 销毁:销毁已初始化的信号量。
· 申请(等待):申请信号量资源。若当前资源数大于0,则资源数减1并继续执行;若资源数等于0,则阻塞等待直到有资源可用。
· 释放(发布):释放信号量资源,资源数加1,并唤醒等待该信号量的线程。
第七部分:关键概念辨析
互斥锁 vs 信号量
· 互斥锁是二元锁,只有锁定和解锁两种状态,适用于保护临界区。
· 信号量是计数器,可允许多个线程同时访问有限资源(如连接池、缓冲池)。
· 互斥锁强调"独占",信号量强调"数量控制"。
死锁 vs 资源竞争
· 资源竞争是正常现象,通过互斥锁可解决。
· 死锁是资源竞争失控导致的永久阻塞状态,需通过设计避免。
分离属性 vs 非分离属性
· 分离属性:线程结束后系统自动回收,无需手动 pthread_join。
· 非分离属性:需手动调用 pthread_join 回收线程空间,否则产生僵尸线程。
原子操作 vs 临界区
· 原子操作是不可分割的最小操作单元,不会被调度打断。
· 临界区是一段代码区域,通过锁机制保证同一时刻最多一个线程执行。