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

Linux软件编程:线程间通信

目录

一、线程间通信基础

1. 概念

2. 通信基础:共享空间

二、互斥锁(Mutex)

1. 概念

2. 使用流程

3. 函数接口

三、死锁

1. 概念

2. 死锁产生的 4 个必要条件

3. 避免死锁的方法

四、信号量(Semaphore)

1. 概念

2. 函数接口

3. 应用场景

一、线程间通信基础

1. 概念

线程间通信指多个线程之间传递信息的过程,是多线程协作完成任务的核心机制

2. 通信基础:共享空间

同一进程中的多个线程共享以下资源,这是线程间通信的基础:

  • 文本段(代码区)
  • 数据段(全局变量、静态变量等)
  • 堆区(动态分配的内存空间)

注意:线程独享栈区(默认 8M),栈区数据不共享;因此线程间通信主要依赖共享的全局变量、堆区数据等

采用全局变量的原因:

  • 进程是操作系统资源分配的最小单元
  • 每个进程空间独立的,包含文本段+数据段(全局变量)+系统数据段
  • 一个进程中的多个线程独享栈空间,文本段、数据段、堆区进程多线程共享

同一进程的线程共享全局变量、静态变量、堆区数据(malloc 分配),但局部变量(栈区)不共享;因此线程间通信需通过共享变量,避免使用栈区数据

  • 多线程同时操作共享空间会引发资源竞争,需要加上互斥锁解决资源竞争问题

二、互斥锁(Mutex)

1. 概念

  • 互斥锁是解决多线程资源竞争的核心机制,可视为一种 "独占性资源"
  • 特性:同一时间只能有一个线程获得锁,加锁期间其他线程需等待解锁后才能再次加锁
  • 临界区:加锁和解锁之间的代码段,即被互斥锁保护的共享资源操作代码
  • 只能防止多个线程对资源的竞争,不能决定代码的先后执行顺序
  • 原子操作:CPU 执行加锁 / 解锁操作时无法切换任务,保证操作的不可分割性

2. 使用流程

1. 定义互斥锁(全局变量)
2. 对锁初始化
3. 操作全局资源前先加锁
4. 如果加锁成功则完成对全局资源操作
5. 如果加锁失败则表示有人占用资源,必须等待其余人释放锁资源才能加锁成功
6. 直到加锁成功使用该全局资源

3. 函数接口

函数名原型功能参数说明返回值
pthread_mutex_initint pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr)初始化互斥锁

mutex

互斥锁变量地址
attr

锁属性(默认 NULL,使用默认属性)

成功返回 0

失败返回 - 1

pthread_mutex_lockint pthread_mutex_lock(pthread_mutex_t *mutex)互斥锁加锁

mutex

互斥锁变量地址

成功返回 0

失败返回 - 1(若锁已被占用,会阻塞等待)

pthread_mutex_unlockint pthread_mutex_unlock(pthread_mutex_t *mutex)互斥锁解锁

mutex

互斥锁变量地址

成功返回 0

失败返回 - 1

pthread_mutex_destroyint pthread_mutex_destroy(pthread_mutex_t *mutex)销毁互斥锁

mutex

互斥锁变量地址

成功返回 0

失败返回 - 1

三、死锁

1. 概念

多线程因加锁 / 解锁顺序错误,导致线程相互等待对方释放锁资源,程序无法继续执行的状态。

2. 死锁产生的 4 个必要条件

条件说明
互斥条件资源(如锁)只能被一个线程占用
不可剥夺条件线程占用的资源不能被强制剥夺
请求保持条件线程持有部分资源,同时请求其他资源
循环等待条件线程间形成资源请求循环(如线程 1 等待线程 2 的锁,线程 2 等待线程 1 的锁)

3. 避免死锁的方法

  1. 保持加锁顺序一致:所有线程按固定顺序加锁(如先锁 A 再锁 B)
  2. 使用非阻塞加锁:用pthread_mutex_trylock(尝试加锁,失败时不阻塞,返回错误码)替代pthread_mutex_lock
  3. 限时加锁:设置加锁超时时间,超时后释放已持有的锁

四、信号量(Semaphore)

1. 概念

  1. 信号量是一种资源
  2. 信号量只能完成四种操作:初始化、销毁、申请、释放
  3. 如果信号量资源数为0,申请资源会阻塞等待,直到占用资源的任务释放资源,资源数不为0才能申请到资源并继续向下执行
  4. 释放资源不会阻塞

2. 函数接口

函数名原型功能参数说明返回值
sem_initint sem_init(sem_t *sem, int pshared, unsigned int value)初始化信号量

sem

信号量变量地址
pshared

0 表示线程间共享,非 0 表示进程间共享
value

初始资源数量

成功返回 0

失败返回 - 1

sem_destroyint sem_destroy(sem_t *sem)销毁信号量

sem

信号量变量地址

成功返回 0

失败返回 - 1

sem_waitint sem_wait(sem_t *sem)申请信号量(P 操作)

sem

信号量变量地址

成功返回 0(资源数 - 1)

失败返回 - 1;资源数为 0 时阻塞

sem_postint sem_post(sem_t *sem)释放信号量(V 操作)

sem

信号量变量地址

成功返回 0(资源数 + 1)

失败返回 - 1

注意:
  • 申请信号量会让信号量资源数-1
  • 如果信号量资源数为0,则会阻塞等待,直到有任务释放资源,才能拿到资源并继续向下 执行

3. 应用场景

  • 控制并发访问数量(如限制同时访问共享内存的线程数)
  • 实现线程间的同步(如生产者 - 消费者模型中控制数据读写顺序)

注意:

  1. 互斥锁与信号量的区别
  • 互斥锁:用于 "独占" 资源(资源数固定为 1),解决资源竞争
  • 信号量:用于 "计数" 资源(资源数可自定义),控制并发数量或同步

     2.临界区设计原则

  • 临界区代码应尽可能精简,仅包含对共享资源的操作,减少线程等待时间,提高效率
http://www.dtcms.com/a/335302.html

相关文章:

  • 【FreeRTOS】队列集
  • MySQL 插入数据提示字段超出范围?一招解决 DECIMAL 类型踩坑
  • 第三十七天(js前端数据加密和混淆)
  • Fixture Caliper 工具
  • GRPO(Group Relative Policy Optimization)公式速览
  • Scala面试题及详细答案100道(11-20)-- 函数式编程基础
  • 嵌入式软件架构设计之九: 双机通信之通信方式
  • 排列与组合
  • 超详细yolov8/11-obb旋转框全流程概述:配置环境、数据标注、训练、验证/预测、onnx部署(c++/python)详解
  • STM32标准库学习笔记
  • MM-Spatial和Spatial-MLLM论文解读
  • 【力扣-多数元素 JAVA/Python】
  • CD4+ T细胞激活区分抗PD-L1联合抗CTLA4疗法与单药抗PD-L1治疗的响应差异-空间最近邻分析
  • 民法学学习笔记(个人向) Part.5
  • 【最后203篇系列】032 OpenAI格式调用多模型实验
  • 39.离散化与哈希
  • 数据结构:二叉树的遍历 (Binary Tree Traversals)
  • 杂记 03
  • v-scale-scree: 根据屏幕尺寸缩放内容
  • 基于Python的电影评论数据分析系统 Python+Django+Vue.js
  • 防御保护12-14
  • tmux常用命令
  • Flamingo
  • KingbaseES主备读写分离集群安装教程
  • 字节数据流
  • 北汽新能源半年报:双品牌战略拉动销量增长,多元布局促进转化
  • PIDGen!DecodeProdKey函数分析之四个断点
  • 【大模型应用开发 3.RAG技术应用与Faiss向量数据库】
  • 【leetcode】12. 整数转罗马数字
  • 关于“双指针法“的总结