当前位置: 首页 > news >正文

Linux操作系统软件编程——多线程

什么是线程

线程的定义

是轻量级的进程,可以实现多任务的并发。

线程是操作系统任务调度的最小单位,一个进程至少有一个线程

线程的创建

由某个进程创建,且进程创建线程时,会为其分配独立的栈区空间(默认8M)。线程和所在的进程,以及进程中的其他线程,共用进程的堆区、文本区、数据区

kernel:内核空间
stack:从高地址向低地址增长的栈区,属于线程独享,存放局部变量、函数的返回地址、函数的参数
heap:从低地址向高地址增长的堆区,用户自行管理的空间,使用时需要手动申请及释放
map:内存映射区,存放动态共享库、文件映射、匿名映射
bss:未初始化及初始化为0的全局及静态变量
data:初始化为非0的全局及静态变量
code:只读代码段,存放可执行的二进制程序

线程的调度

宏观并行,微观串行

进程和线程的区别

进程线程
定义正执行中的程序程序中的一条执行线路
单位操作系统资源分配的最小单位操作系统任务调度的最小单位
资源消耗资源消耗开销大,每次创建都需要有0-4G的虚拟内存空间资源消耗开销较小,只需要由所在进程为其开辟默认8M的栈区空间
效率由操作系统创建,创建耗时大,跨进程调度慢由进程创建,创建耗时小,跨线程调度快
通信通信复杂,进程间不能直接通信,需要使用进程间通信机制(IPC)通信简单,可以使用线程共享的区域通信(例如全局变量)
安全性安全性和稳定性较高,因为各个进程空间独立安全性和稳定性较低,一个线程异常,可能影响同一进程中的线程

线程相关编程

线程的创建:pthread_create();pthread_self():获取当前线程的ID号

线程的调度:由操作系统调度

线程的凋亡:

1,线程退出:在线程任务函数中使用return结束线程或者调用pthread_exit()结束线程

2,线程回收:pthread_join(tid,NULL)

pthread_create()

功能:创建一个新的线程

参数:

thread:保存线程ID的变量地址

attr:线程属性的对象地址,如果是NULL则按照默认属性创建

void *(*start_routine)(void *):函数指针,指向线程启动后要执行的任务

指针名称:start_routine

指向的对象:函数void *()(void *)

即仅传函数名,如果在函数名后加括号并传参,则实际上传入的是该函数的返回值,并非该函数

arg:为线程任务函数传递的参数,如果不传参则传NULL

如果要传入多个参数,可以将多个参数组合到一起为一个结构体,然后传入结构体地址

返回值:成功则为0,失败则为!0

需要注意的是,在使用这个函数时,需要在创建或者编译时加上后缀,如图所示

当进程中创建了线程,不要直接退出进程(线程之后就接return),在return之前设立一个死循环或者sleep,否则可能线程还未执行,进程就优先结束,连带着线程也结束

下面的两种情形值得说明一下

对于第一个参数,它需要的是一个存放ID的变量,即将该函数创建的线程的ID号存到变量中,通过传入这个变量的地址来确定存到哪个变量中,它并不是把这个变量的地址赋到一个指针中,所以第二种情况是错误的,因为此时的thread属于野指针,会把ID号赋给这个指针,程序出现段错误

对于部分其他函数,也会出现这种情况。

pthread_exit()

功能:退出一个线程任务

参数:向回收的线程传递的参数的地址,如果不传递参数则为NULL

pthread_exit(NULL)等价于return NULL

pthread_join()

功能:阻塞等待回收线程资源空间

参数:要回收的线程ID;用来保存线程退出时传递的参数(与上一个函数搭配),NULL表示不接收传递的参数

返回值:成功为0,失败为-1

线程属性

1,分离属性:不需要被其他线程回收的线程,将来会被操作系统回收

2,非分离属性:可以被其他线程回收或者结束的线程,默认属性为非分离属性

线程回收策略

1,分离属性的线程:不需要回收(没有空闲的线程可以帮忙回收)

2,调用pthread_join阻塞回收

int pthread_detach(pthread_t thread)

功能:将线程设置成分离属性的线程

线程间通信

临界资源:多个线程可以同时访问的资源,例如全局变量,共享内存区域

然而,在多个线程在访问临界资源时,会存在资源竞争问题,如下

如果按照代码正常思路,最后输出应该是200000,但是最后却输出199997,这是由于对于全局变量n,其中一个线程调用并进行操作,还未将结果重新赋予时,另一个线程也进行了调用,导致两个线程赋予了同一个值。

事实上,数字越大越容易出现这种情况,但是并不代表每次运行都会出现这种情况,所以为了完全避免这种资源竞争问题,需要用到互斥机制

互斥机制——锁

互斥机制:多个线程访问临界资源时,具有排他性访问的机制,即一次只允许一个线程对该临界资源进行访问

实现互斥机制的步骤

1,创建互斥锁:pthread_mutex_t

2,初始化互斥锁:pthread_mutex_init

3,锁上:int pthread_mutex_lock(pthread_mutex_t *mutex)

4,解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex)

5,销毁锁:int pthread_mutex_destroy(pthread_mutex_t *mutex)

pthread_mutex_init

功能:初始化互斥锁

参数:锁住对象的地址;锁的属性,如果是NULL则为默认属性

返回值:成功则为0,失败则为-1

所以上述的例子可改为

死锁

指的是在多线程环境中,每个执行流(线程)都有未释放的资源,且互相请求对方未释放资源,从而导致陷入永久等待状态的情况。锁包括互斥锁、信号量、自旋锁、读写锁等等

现象:

1)没有释放锁

2)重复加锁

3)多线程多锁,抢占锁资源不当

例:线程A获得了1锁,B获得了2锁,同时线程A向获得2锁,B想获得1锁

产生死锁的必要条件

1,互斥条件 :一个资源只能被一个进程使用(一个执行流获取锁后,其他执行流不能再获取该锁)

2,请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放(执行流本身使用者一把锁并不释放,还请求别的锁)

3,不剥夺条件:进程已获得的资源,再未使用完之前,不能强行剥夺(A执行流拿着锁,其他执行流不能释放)

4,循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系(多个执行流拿着对方想要的锁,并且各执行流需要获取对方已持有的锁才能继续执行

四个必要条件都达成时候一定是死锁,有一种条件未达成时就构不成死锁

死锁的预防

1,锁一定要成对出现,即有pthread_lock就一定要有pthread_unlock

2,使线程的加锁顺序一致

3,破坏环路等条件。使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的锁,例如pthread_mutex_trylock(),int sem_trywait(sem_t *sem)

线程间的同步机制——信号量

让多个线程再执行某个任务时,具有先后顺序地执行,而多线程、多进程本质是异步执行

线程间的同步通过信号量实现,步骤如下

1)定义信号量对象:sem_t sem

2)初始化信号量:sem_init()

3)PV操作:申请信号量(P),释放信号量(V):sem_wait(),sem_post()

4)销毁信号量:int sem_destroy(sem_t *sem)

int sem_init(sem_t *sem, int pshared, unsigned int value)

功能:对信号量进行初始化

参数:

sem:要初始化的信号量对象地址

pshared:0为线程间共享,非0为进程间共享

value:信号量初始值,如果是0则不执行,1则执行

返回值:成功为0,失败为-1

int sem_wait(sem_t *sem)

功能:申请信号量

int sem_post(sem_t *sem)

功能:释放信号量

左侧为加了信号量的代码,右侧为未加信号量的代码

代码的公共区域

未加信号量的代码是异步的,没有先后输出顺序

加了信号量的代码是同步的,即有输出的先后顺序

值得注意的是,一定要先初始化再执行PV操作。为保证线程执行的顺序,PV操作时,P的是该线程的信号量并减一,V的是下一个该执行的线程的信号量并加一

在进行P操作时,会进行阻塞,即如果该信号量为0,则会等待信号量变为1再执行

一般来说,pthread_join会放在sem_destroy之前,来确保所有线程结束,不会再有线程对信号量进行操作,然后再进行信号量的销毁

http://www.jsqmd.com/news/803917/

相关文章:

  • 算法题(176):three states
  • 2026年南京专业留学中介机构前十强全面解析 - 速递信息
  • 清镇名表回收技术全解析:清镇靠谱的黄金回收/清镇高价回收黄金/清镇黄金回收上门/清镇黄金回收正规/清镇黄金回收靠谱/选择指南 - 优质品牌商家
  • 2026年5月邢台启闭机/螺杆启闭机/斜拉启闭机/手电螺杆启闭机/双吊点卷扬启闭机厂家解析,认准新河县全方水工机械厂 - 2026年企业推荐榜
  • 告别串口打印!用STM32CubeMonitor实时可视化你的变量波形(附F4正弦波Demo)
  • 利用taotoken模型广场为ai应用快速进行模型选型与测试
  • 动作设计模式:HTTP API动作标准化终极指南
  • 厚街吉他培训哪家值得推荐:秒杀吉他培训 服务贴心 - 19120507004
  • Diem隐私计算:安全多方计算在区块链中的终极应用指南
  • 管理多个APIKey并设置访问控制与审计日志
  • 2026年Q2常德无人机培训专业选择核心技术维度解析:怀化无人机培训/株洲无人机培训/永州无人机培训/湘潭无人机培训/选择指南 - 优质品牌商家
  • 2026油电混合SUV推荐:可油可电可增程,一台车覆盖全场景 - 速递信息
  • 使用Node.js和Taotoken快速构建一个AI对话微服务
  • 如何为现有基于OpenAI SDK的项目无缝迁移到Taotoken聚合平台
  • 【实战篇 / ZTNA】(7.0) ❀ 从零部署:FortiClient EMS 7.0 与 FortiGate 的联动配置 ❀ 零信任网络访问
  • ComfyUI-WanVideoWrapper终极指南:3个技巧解决AI视频生成难题
  • Midjourney Ziatype印相全流程实战手册(含官方未公开--style raw适配矩阵与gamma校准表)
  • 浙江音乐学院校考培训核心技术要点与备考路径解析:浙江音乐艺考机构、浙江音乐艺考集训、杭州器乐艺考培训、杭州声乐艺考培训选择指南 - 优质品牌商家
  • RPGMZ 插件制作教程 如何保存变量值到游戏存档
  • 劝!别直接用AI写论文!深扒毕业之家和PaperRed哪个才是真降重[特殊字符]
  • 2026年北京留学中介机构对比,反馈及时哪些比较好值得关注 - 速递信息
  • 【信息科学与工程学】【管理科学】第七十篇 中国主要类型企业的交易与利益交换/利益输送模型02
  • 2026年安徽二手PCB设备买卖与产能扩充完全指南 - 优质企业观察收录
  • 12-production-best-practices 生产实践:观测、安全、成本、评测和持续演进
  • ASN.1编解码实战:从协议规范到C语言实现
  • 如何快速掌握QQ截图独立版:Windows平台终极截图与OCR识别工具完全指南
  • 选购鸟牌Bird功率计,这些型号值得了解——总代理深圳新朗普的一手推荐 - 品牌推荐大师1
  • 2026天津大牌首饰哪里估价靠谱?卡地亚宝格丽实地探店 - 奢侈品回收测评
  • Hermes Agent 可视化监控与文档生成工具 hermes-dashboard 详解
  • 2026年住校生卫生巾囤货:高性价比品牌选型指南 - 产业观察网