线程(操作系统任务调度的最小单位)

什么是线程?

线程是轻量化的进程,用来实现多任务并发

线程是操作系统任务调度的最小单位

线程由进程创建,在创建时会在栈区为线程分配独立的栈区空间(默认8M)

在同一个进程中的线程共用堆区、数据区、文本区

进程线程的区别

进程线程
正在执行的程序轻量级的进程
操作系统资源分配的最小单位操作系统任务调度的最小单位
资源空间消耗大(0-4G)资源空间消耗小(8M的栈区,其他区域共享)
进程(创建、任务切换)效率低线程(创建、任务切换)效率高
进程安全性高(空间独立)线程的安全性较低(有共享空间)
进程间通信较为复杂、需要用到IPC机制线程间通信简单(共享内存)
在相同资源平台下,多进程的并发量少于多线程

线程相关接口

<span style="background-color:#f8f8f8"><span style="color:#333333">#include <pthread.h>
​
// 1. 线程的创建
// 参数: (线程ID指针, 线程属性, 线程函数入口, 传给线程函数的参数)
// 返回值: 成功返回0, 失败返回错误码(非零)
pthread_create(&tid, NULL, thread_func, (void*)arg);
​
// 2. 线程的退出
// 参数: 线程的返回值(可由其他线程通过pthread_join接收)
// 作用: 终止调用该函数的线程, 并返回retval给等待者
pthread_exit(void *retval);
​
// 3. 获取当前线程自身的ID (您留空的部分)
// 返回值: 调用该函数的线程的线程ID (pthread_t类型)
pthread_t pthread_self(void);
​
// 4. 等待线程结束 (线程回收)
// 参数: (要等待的线程ID, 接收线程返回值的指针)
// 返回值: 成功返回0, 失败返回错误码
// 作用: 阻塞调用线程, 直到目标线程终止, 并回收其资源
pthread_join(pthread_t thread, void **retval);</span></span>

注意:链接的时候必须显式链接 -lpthread

线程的分离属性

分离属性:不需要回收的线程,称为具有分离属性的线程,结束时可以被操作系统回收。

非分离属性:可以被其他线程回收或者结束的线程成为非分离属性的线程。(默认的属性)

线程的互斥

互斥锁 - 保护共享资源,防止并发访问冲突

<span style="background-color:#f8f8f8"><span style="color:#333333">pthread_mutex_init()     // 初始化
pthread_mutex_lock()     // 加锁(阻塞)
pthread_mutex_trylock()  // 加锁(非阻塞)
pthread_mutex_unlock()   // 解锁
pthread_mutex_destroy()  // 销毁</span></span>

条件变量 - 等待某个条件成立(必须配合互斥锁)

条件变量就是为了避免CPU空转轮询而设计的,让线程在条件不满足时安心"睡觉",条件满足时被"叫醒"

<span style="background-color:#f8f8f8"><span style="color:#333333">pthread_cond_init()      // 初始化
pthread_cond_wait()      // 等待条件(自动释放锁)
pthread_cond_timedwait() // 限时等待
pthread_cond_signal()    // 唤醒一个等待线程
pthread_cond_broadcast() // 唤醒所有等待线程
pthread_cond_destroy()   // 销毁</span></span>

线程间的同步机制

信号量 - 管理资源数量(独立使用,无需互斥锁)

<span style="background-color:#f8f8f8"><span style="color:#333333">sem_init()       // 初始化(初始资源数)
sem_wait()       // P操作:资源数-1(为0则阻塞)
sem_trywait()    // 非阻塞版
sem_timedwait()  // 限时版
sem_post()       // V操作:资源数+1(唤醒等待者)
sem_getvalue()   // 获取当前计数
sem_destroy()    // 销毁</span></span>

死锁的产生条件

死锁是指两个或多个线程(进程)互相等待对方持有的资源,导致所有线程都无法继续执行。

四个必要条件(必须同时满足)

条件说明比喻

1. 互斥

(Mutual Exclusion)

资源一次只能被一个线程占用厕所只有一间,一次只能进一个人

2. 持有并等待

(Hold and Wait)

线程持有资源的同时,等待其他资源你占着厕所A,还等着厕所B空出来

3. 不可抢占

(No Preemption)

已分配的资源不能被强制剥夺别人不能把你从厕所里拽出来

4. 循环等待

(Circular Wait)

线程间形成循环等待链A等B,B等C,C等A,都在等对方让出资源

只要破坏任意一个条件,死锁就不会发生!

如何避免死锁(5种策略)

  1. 破坏"循环等待":统一加锁顺序(最常用)

  1. 破坏"持有并等待":一次性申请所有资源

  1. 破坏"不可抢占":使用 trylock + 回退

  1. 使用递归锁

  1. 使用更高层级的同步机制

  • 信号量:管理资源计数,自然避免死锁

  • 条件变量:配合互斥锁使用,但设计合理可避免死锁

  • 读写锁:允许多个读操作并发

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐