嵌入式从0到精通——线程
📝引言
进程是操作系统资源分配的最小单位,但是进程创建、切换开销大。线程作为轻量级进程,是 CPU 任务调度的基本单元,同一个进程内多个线程共享进程绝大多数资源,线程切换开销远小于进程。本篇讲解 Linux 下线程基础、线程创建回收、线程属性、线程互斥锁,帮助掌握多线程并发编程
1. 什么是线程
线程是一个轻量级的进程,用来实现多任务并发。 线程是操作系统任务调度的最小单位;进程是操作系统资源分配的最小单位。
线程由所在进程创建,线程一定隶属于某个进程。 线程创建的时候,操作系统会为每一个线程分配独立栈空间(默认 8M);同一个进程内所有线程共享进程的堆区、数据段、bss 段、代码段、内核空间,仅栈区互相独立。

2. 进程与线程对比
| 对比项 | 进程 | 线程 |
|---|---|---|
| 核心概念 | 操作系统资源分配的最小单位 | CPU 任务调度的最小单位 |
| 创建切换效率 | 创建开销大,完整复制虚拟内存;切换速度慢 | 创建快,仅分配栈;线程间切换速度快 |
| 内存占用 | 完整 0‑4G 虚拟地址空间,内存消耗大,并发数量受限 | 仅拥有独立栈区,其余内存和进程共享,资源开销小 |
| 安全性 | 进程之间地址空间完全独立,一个进程崩溃不影响其他进程 | 共享进程地址空间,任意一个线程崩溃会导致整个进程全部退出 |
| 通信方式 | 不能直接访问,必须使用 IPC 进程间通信机制 | 同进程线程直接读写全局变量、堆内存即可完成通信 |
3. 线程调度
宏观并行,微观串行。CPU 时间片轮转调度,调度算法和进程保持一致。
4. 线程消亡
- 线程执行函数 return 返回,线程结束
- 整个进程退出,进程内所有线程全部直接消亡
- 调用
pthread_exit()主动退出当前线程
5. 线程常用 API
5.1 pthread_create 创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
- 功能:创建并且启动一个子线程
- 参数
thread:输出参数,保存线程 IDattr:线程属性,填 NULL 使用默认属性start_routine:线程执行函数,函数格式固定void* func(void*)arg:传递给线程函数的入参
- 返回值:成功返回 0;失败返回错误码(非 0)
⚠️编译注意:线程库不属于标准 C 库,编译链接必须加
-pthread。主线程不能在线程执行完毕前直接退出,否则子线程直接销毁。
5.2 线程分离属性
线程默认是非分离属性:线程退出之后资源不会自动释放,必须其他线程调用pthread_join回收;如果不回收会产生资源泄露。
pthread_detach(tid):设置线程为分离属性;线程结束操作系统自动回收资源,不需要 join 回收。 适用场景:主线程没有机会去阻塞等待子线程结束。
5.3 pthread_join 阻塞回收线程
int pthread_join(pthread_t thread, void **retval);
- 功能:阻塞等待指定线程结束,回收线程资源
- 参数
- thread:待回收线程 ID
- retval:接收线程返回值,不关心返回值填 NULL
- 返回值:成功 0,失败非 0
6. 线程同步互斥:互斥锁
多线程并发访问临界资源(全局变量、队列、文件、数据库)会产生资源竞争,数据错乱。
临界资源:多个线程都可以访问的资源;访问临界资源的代码片段称为临界区。

互斥锁作用:保证同一时间,只有一个线程进入临界区访问资源,实现访问排他性。
互斥锁操作流程:
- 定义锁对象:
pthread_mutex_t mutex; - 初始化锁:
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,失败错误码
拓展其他锁类型:
- 读写锁:读共享,写独占;适合读多写少场景
- 自旋锁:获取不到锁不会休眠,持续占用 CPU 循环轮询
7. 信号量实现线程同步
信号量可以控制访问资源线程数量,实现线程同步。
int sem_init(sem_t *sem, int pshared, unsigned int value);
- sem:信号量对象
- pshared:0 代表线程间共享;非 0 代表进程间共享
- value:信号量初始资源计数 返回:成功 0,失败非 0
信号量配套接口
sem_wait():P 操作,资源计数‑1;资源为 0 时阻塞等待sem_post():V 操作,资源计数 + 1,唤醒等待线程sem_destroy():销毁信号量
8. 死锁产生条件以及解决
死锁:多线程场景,各个线程已经持有部分锁资源,同时互相请求对方持有的锁,全部永久阻塞。
死锁 4 个必要条件,破坏任意一条即可消除死锁:
- 互斥条件:资源同一时刻只能被一个线程占有(锁本身特性)
- 请求与保持条件:线程已经占有锁,同时申请新锁,原有锁不释放
- 不剥夺条件:锁只能持有者主动释放,其他线程不能强制抢夺锁
- 循环等待条件:多个线程形成环形互相等待锁资源
常见死锁场景:忘记解锁;重复加锁;多把锁加锁顺序混乱。
解决思路:
- 加锁解锁必须成对出现
- 多线程操作多把锁,所有线程加锁顺序保持完全一致,破坏循环等待
- 使用非阻塞锁
pthread_mutex_trylock()、sem_trywait(),申请失败主动释放已经获取资源
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)