Linux应用软件编程(多任务并发:线程)
2. 线程
1. 什么是线程
线程是一个轻量级的进程,用来实现多任务并发。
线程是操作系统任务调度的最小单位。
线程由某个进程创建。
2. 线程的创建
线程由所在进程创建,创建时,进程为其分配独立的栈区空间(默认8M)。
同一个进程中的线程,共用所在进程的堆区、数据区、文本区。

3. 线程的调度
宏观并行
微观串行
4. 线程消亡
线程退出
回收线程的资源空间
5. 进程和线程的区别*
| 进程 | 线程 |
|---|---|
| 正在执行的程序 | 轻量级的进程 |
| 操作系统资源分配的最小单位 | 操作系统任务调度的最小单位 |
| 资源空间消耗大(0-4G虚拟内存空间) | 资源空间消耗小(栈区独立,其他区域共享) |
| 进程效率低(创建、任务切换) | 线程效率高(创建、任务切换) |
| 进程安全性高(进程空间独立) | 线程安全性低 |
| 进程间通信较复杂,需要用到IPC机制 | 线程间通信简单(全局变量) |
| 在相同资源平台下,多进程的并发量少于多线程 |
6. 线程相关接口
-
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 :函数指针,线程启动后要执行的任务的函数地址(线程任务函数)
arg :传递给线程任务函数的参数
返回值:
成功:0
失败:非0
注意:1. 编译和链接时要加-lpthread, 表示链接线程库。
2. 线程执行时,线程所在的进程不能先退出。
-
pthread_exit();
void pthread_exit(void *retval);
功能:结束一个线程
参数:
retval :可以用来保存线程退出时传递的参数
和return返回的指针类似
注意:
线程退出:
1. 在线程任务中return
2. 在线程任务中pthread_exit
3. 在线程中exit,让所在进程直接退出
phtread_self();
pthread_join();
int pthread_join(pthread_t thread, void **retval);
功能:等待回收具有非分离属性的线程
参数:
thread:要回收的线程ID;
retval:接收回收的线程退出时传递的参数:通过pthread_exit参数,return返回值传递
NULL : 不接收参数
返回值:
成功:0
失败:非0
7. 线程的分离属性
分离属性 :不需要被其他线程回收的线程,称为具有分离属性的线程,结束时可以被操作系统回收。
非分离属性:可以被其他线程回收或者结束的线程称为非分离属性的线程
默认属性:非分离属性
设置线程分离属性:
int pthread_detach(pthread_t thread);
功能:设置线程的分离属性
参数:
thread:要设置的线程的线程ID
返回值:
成功:0
失败:!0
8. 线程的互斥机制
多线程访问临界资源时,存在资源竞争问题。
临界资源:多个线程可以同时操作的资源,比如:全局变量,共享内存等。

如何解决资源竞争问题:
线程的互斥机制:让多个线程在访问临界资源时,具有排他性访问的特性
互斥机制:互斥锁、信号量、读写锁(写独占,读共享)、自旋锁
互斥锁:实现互斥机制
-
创建互斥锁:pthread_mutex_t
-
初始化锁:pthread_mutex_init
-
加锁:pthread_mutex_lock/pthread_mutex_trylock
-
解锁:pthread_mutex_unlock
-
销毁锁:pthread_mutex_destroy
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
功能:初始化互斥锁
参数:
mutex :互斥锁对象地址
attr: 锁的属性
返回值:
成功:0
失败:非0
int pthread_mutex_lock(pthread_mutex_t *mutex); 功能:以阻塞方式等待加锁 int pthread_mutex_trylock(pthread_mutex_t *mutex); 功能:以非阻塞方式加锁,如果该锁已被使用,则返回 int pthread_mutex_unlock(pthread_mutex_t *mutex); 功能:解锁/释放锁 int pthread_mutex_destroy(pthread_mutex_t *mutex); 功能:销毁锁
9. 线程间同步机制
线程间同步:让多个线程任务执行某部分任务的时候具有先后顺序。
同步方式:信号量
可以将信号量看成一个资源数,当该资源数>0的时候,可以申请到信号量,当资源时为0时,申请信号量阻塞等待。申请完信号量时,该信号量对应的资源数自动-1。释放对应的信号量时,该信号量对应的资源数自动+1。

-
创建信号量:sem_t
-
初始化信号量:
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化信号量
参数:
sem:初始化的信号量对象地址
pshared:进程间共享还是线程间
0: 线程间
非0:进程间
value:初始化的资源数
返回值:
成功:0
失败:-1
-
申请信号量(P操作): int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem);
-
释放信号量(V操作) int sem_post(sem_t *sem);
-
销毁信号量 int sem_destroy(sem_t *sem);
10. 死锁
死锁:死锁指的是在多线程环境中,每个执行流(线程)都有未释放的资源,且互相请求对方未释放资源,从而导致陷入永久等待状态的情况。
现象:
现象1:忘记释放锁
现象2:重复加锁
现象3:多线程多锁,抢占锁资源不当
如:线程A获取了1锁,线程B获取了2锁,同时线程A还想获取2锁,线程B还想获取1锁
*产生死锁的四个必要条件(四条全中 = 死锁,破掉任意一条 = 解除):
(1) 互斥条件:一个资源同一时刻只能被一个任务占用(一个执行流获取锁后,其它执行流不能再获取该锁)。
(2) 请求与保持条件:已经拿着一部分资源不释放,同时又去申请新的资源(执行流本身使用着一把锁并不释放,还在请求别的锁)。
(3) 不剥夺条件:资源只能由持有者主动释放,系统/其他任务不能强行抢过来(A执行流拿着锁,其它执行流不能释放)。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系(多个执行流拿着对方想要的锁,并且各执行流还去请求对方的锁)。
解决方法:
1.锁一定要成对出现
2.使线程的加解锁顺序一致
3.破坏环路等待条件
使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的资源
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)