一、线程的概念
线程是轻量级的进程,用于实现多任务并发。
- 线程:操作系统CPU任务调度的最小单位
- 进程:操作系统资源分配的最小单位
二、线程的创建
线程隶属于某一个进程,由所属进程创建。
- 线程会分配独立栈空间(默认8M)
- 同进程下所有线程共享堆区、数据区、文本代码区、内核空间。
三、进程与线程的区别
| 对比项 | 进程 | 线程 |
|---|---|---|
| 概念 | 操作系统资源分配的最小单位 | CPU任务调度的最小单位 |
| 效率 | 创建开销大 | 创建速度快,切换开销小 |
| 内存 | 内存消耗大,并发数量受限 | 仅独立栈区,其余资源和进程共享 |
| 安全性 | 进程空间完全独立,安全性高 | 共享地址空间,安全性较差 |
| 通信 | 不能直接通信,依赖IPC进程间通信 | 可直接通过全局变量、队列通信 |
四、线程的调度
调度逻辑和进程相同:宏观并行,微观串行。
五、线程消亡
两个阶段:
- 线程退出
- 回收线程资源空间
六、线程相关函数接口
1. pthread_create 创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);参数说明:
thread:保存线程ID的变量地址attr:线程属性,传NULL使用默认属性start_routine:线程任务函数指针,函数格式void *func(void *)arg:传给线程任务函数的参数
返回值:成功返回0;失败返回非0错误码。
⚠重要注意
- 编译线程代码必须链接线程库:
-lpthread/-pthread- 主线程不要在线程执行完毕前直接结束进程,否则所有线程会直接销毁。
2. 线程退出的几种方式
- 线程任务函数执行结束
return - 整个进程退出
- 调用
pthread_exit()主动退出线程
七、线程的分离属性与非分离属性
- 分离属性 detach:线程结束由操作系统自动回收,其他线程不需要调用
pthread_join回收。
int pthread_detach(pthread_t tid);适用场景:其他线程没有机会做回收操作。
- 非分离属性(默认):线程结束后资源不会自动释放,必须调用
pthread_join回收。
int pthread_join(pthread_t thread, void **retval);参数:
thread:待回收线程IDretval:接收线程返回状态,不关心状态填NULL返回:成功0,失败非0。特性:阻塞等待目标线程结束。
八、线程的同步和互斥
1. 线程间通信
通信手段:全局变量、全局队列、pthread_create传参、条件变量。
临界资源:多个线程都可以访问的资源,例如全局变量、队列、文件。 临界区:访问临界资源的那一段代码。 多线程同时访问临界资源会产生资源竞争问题。
2. 互斥机制:互斥锁
实现多个线程排他访问临界资源。 使用步骤:
- 定义锁对象
pthread_mutex_t pthread_mutex_init()初始化锁pthread_mutex_lock()加锁pthread_mutex_unlock()解锁pthread_mutex_destroy()销毁锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);mutex:锁对象地址attr:锁属性,NULL使用默认属性 返回:成功0,失败非0。
其他锁类型:
- 读写锁:读共享,写独占
- 自旋锁:获取不到锁时,CPU空循环,不放弃CPU时间片
3. 同步机制:信号量semaphore
控制线程执行先后顺序,实现同步。 步骤:
- 创建信号量对象
sem_t sem_init()初始化信号量sem_wait()P操作(申请信号量)sem_post()V操作(释放信号量)sem_destroy()销毁信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);参数:
sem:信号量对象地址pshared:0用于线程之间;非0用于进程之间value:信号量初始值 返回:成功0,失败非0。
九、死锁
死锁:多线程环境,各个线程持有资源,又互相请求对方手里的资源,全部陷入永久等待。
常见死锁现象
- 忘记解锁
- 同一个锁重复加锁
- 多把锁,线程加锁顺序混乱(线程A拿锁1,线程B拿锁2;A请求锁2,B请求锁1)
死锁四个必要条件(全部满足才会死锁,破坏任意一条即可解除)
- 互斥条件:资源同一时刻只能被一个任务占用。
- 请求与保持条件:持有部分资源不释放,同时申请新资源。
- 不剥夺条件:资源只能持有者主动释放,别人不能强行抢夺。
- 循环等待条件:多个线程形成环路互相等待对方资源。
解决死锁方法
- 加锁解锁成对编写,不要漏写解锁。
- 所有线程加锁顺序保持一致,破坏循环等待。
- 使用非阻塞接口:
pthread_mutex_trylock()、sem_trywait(),拿不到锁主动释放已持有的锁资源。