news 2026/3/6 0:39:26

系统编程—线程的互斥与同步

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
系统编程—线程的互斥与同步

线程的互斥和同步是多线程编程的核心问题,用于解决资源竞争和执行时序协调的问题,确保多线程程序的正确性、稳定性和可预测性。

核心概念铺垫

  • 临界区(Critical Section):多个线程共享的资源(如全局变量、硬件设备、文件句柄)或操作这些资源的代码段,同一时间只能被一个线程执行,否则会引发数据错乱(如 “脏读”“重复写”)。
  • 竞态条件(Race Condition):多个线程同时访问临界区,且执行顺序不可控,导致程序输出结果依赖于线程调度顺序的错误现象(是互斥要解决的核心问题)。

互斥

1.定义

互斥是指:禁止多个线程同时进入同一临界区,保证临界区的 “排他性访问”,本质是解决 “资源竞争” 问题。
简单说:互斥是 “不许同时干”,核心是 “抢资源” 的问题。

2. 常见实现方式

(1)互斥锁(Mutex)

最常用的互斥机制,本质是一个 “锁标记”:

  • 线程进入临界区前加锁lock):如果锁未被占用,成功加锁并进入;如果已被占用,线程阻塞等待,直到锁被释放。
  • 线程离开临界区后解锁unlock):释放锁,让等待的线程竞争获取。
  • 互斥的使用步骤:定义互斥锁 ==》初始化锁==》加锁==》解锁==》销毁

特性

  • 互斥锁是 “非递归” 的(默认):同一线程重复加锁会导致死锁。
  • 支持 “公平 / 非公平” 调度:公平锁按等待顺序唤醒线程,非公平锁随机唤醒(效率更高)。
(2)自旋锁(Spin Lock)

与互斥锁的区别:线程获取不到锁时,不阻塞,而是循环(自旋)检查锁是否释放,直到获取到锁。

  • 适用场景:临界区执行时间极短(如几纳秒),避免线程上下文切换的开销。
  • 缺点:自旋会占用 CPU 资源,临界区耗时过长时会导致 CPU 利用率飙升。
(3)其他互斥机制
  • 信号量(Semaphore):初始值为 1 的信号量可作为互斥锁(二值信号量);
  • 原子操作(Atomic):对简单数据类型(如 int、bool)的操作,通过 CPU 指令保证原子性(无需加锁,效率更高)。

特性:

  • 互斥锁是 “非递归” 的(默认):同一线程重复加锁会导致死锁。
  • 支持 “公平 / 非公平” 调度:公平锁按等待顺序唤醒线程,非公平锁随机唤醒(效率更高)。

相关函数

1、定义:
pthread_mutex_t mutex;
2、初始化锁
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
功能:将已经定义好的互斥锁初始化。
参数:
  • mutex要初始化的互斥锁
  • atrr初始化的值,一般是NULL表示默认锁
返回值:
  • 成功0
  • 失败 非零
3、加锁:
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:
  • 用指定的互斥锁开始加锁代码
  • 加锁后的代码到解锁部分的代码属于***原子操作***
  • 在加锁期间其他进程/线程都不能操作该部分代码
  • 如果该函数在执行的时候,mutex已经被其他部分使用则代码阻塞。
参数:mutex用来给代码加锁的互斥锁
返回值:
  • 成功0
  • 失败 非零
原子操作: 在线程的一次调度中,这段代码必须完成,不能发生线程调度。
4、解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:
  • 将指定的互斥锁解锁。
  • 解锁之后代码不再排他访问,一般加锁解锁同时出现。
参数:用来解锁的互斥锁
返回值:
  • 成功0
  • 失败 非零
5、销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:使用互斥锁完毕后需要销毁互斥锁
参数:mutex要销毁的互斥锁
返回值:
  • 成功0
  • 失败 非零

同步

1. 定义

同步是指:协调多个线程的执行顺序,让线程按预期的时序执行(比如 “A 线程执行完某步后,B 线程才能执行”),本质是解决 “执行时序” 问题。

简单说:同步是 “按顺序干”,核心是 “等通知” 的问题。

步骤:信号量的定义 =》信号量的初始化==》信号量的PV操作=》信号量的销毁。

2. 常见实现方式

(1)条件变量(Condition Variable)

条件变量结合互斥锁使用,实现 “线程等待某个条件满足后再执行”。

  • 核心操作:wait()(线程阻塞,释放锁)、notify_one()/notify_all()(唤醒等待的线程)。
(2)信号量(Semaphore)

信号量可实现更灵活的同步:

  • 计数信号量:初始值为 N,表示最多允许 N 个线程同时访问资源;
  • 同步信号量:初始值为 0,实现 “生产者 - 消费者” 等时序协调(生产者生产后post,消费者wait后消费)。
(3)屏障(Barrier)

屏障让多个线程在某个点 “同步等待”,直到所有线程都到达该点后,再继续执行。

  • 适用场景:多线程分阶段任务(如 “所有线程完成初始化后,再执行核心逻辑”)。

相关函数

1、信号量的定义 :
sem_t sem;
2、信号量的初始化:
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:将已经定义好的信号量赋值。
参数:
  • sem要初始化的信号量
  • pshared = 0 ;表示线程间使用信号量
  • pshared != 0 ;表示进程间使用信号量
  • value信号量的初始值,一般无名信号量
  • 都是二值信号量,0 1
  • 0表示红灯,进程暂停阻塞
  • 1表示绿灯,进程可以通过执行
返回值:
  • 成功0
  • 失败-1
3、信号量的PV操作

P ===》申请资源===》申请一个二值信号量
V ===》释放资源===》释放一个二值信号量
P操作对应函数==sem_wait();
V操作对应函数==sem_post();
int sem_wait(sem_t *sem);
功能:
判断当前sem信号量是否有资源可用。如果sem有资源(==1),则申请该资源,程序继续运行 。如果sem没有资源(==0),则线程阻塞等待,一旦有资源则自动申请资源并继续运行程序。
注意:sem申请资源后会自动执行sem = sem - 1;
参数:sem要判断的信号量资源
返回值:
  • 成功0
  • 失败-1
int sem_post(sem_t *sem);
功能:函数可以将指定的sem信号量资源释放并默认执行,sem = sem+1; 线程在该函数上不会阻塞。
参数:sem要释放资源的信号量
返回值:
  • 成功0
  • 失败-1
4、信号量的销毁
int sem_destroy(sem_t *sem);
功能:使用完毕将指定的信号量销毁
参数:sem要销毁的信号量
返回值:
  • 成功0
  • 失败-1

互斥和同步的区别

1.互斥锁,加锁和解锁 是同一个线程
信号量(同步)th1释放th2, th2释放th1.是由线程交叉释放。
2.在互斥锁保护的代码中(临界区)。不要休眠,不要大耗时的操作。临界区代码短小精悍
信号量,适当可以有休眠,小的耗时操作。
用法: 计数信号量 。信号量的初值(3,5)是可以大于1的。 这种情况,用于互斥的情况,资源数本身不唯一(多个资源);
互斥同步
核心目标解决资源竞争(排他访问)解决执行时序(协调顺序)
关注点临界区的 “独占性”线程间的 “依赖性”
典型场景多线程修改同一变量生产者 - 消费者、等待通知
实现基础互斥锁、自旋锁、原子操作条件变量、信号量、屏障

死锁

定义

由于锁资源安排的不合理(锁资源的申请和释放逻辑不对),导致进程,线程无法正常继续执行(
)的现象。

产生死锁的四个必要条件

  1. 互斥条件:一个资源每次只能被一个进程使用。
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/3 19:22:29

【EF Core】通过 DbContext 选项扩展框架

本来老周计划在 10 月 1 日或 2 日写这篇水文的,没打算出去玩(确实没啥好玩)。不过因为买的运动相机到手,急着想试试效果,于是就备了些干粮,骑着山地车在外面鬼混了一天。10 月 2 日,家里来了三…

作者头像 李华
网站建设 2026/3/3 14:09:44

新用户免费试用EmotiVoice 1000个token

EmotiVoice:用1000个免费Token开启高表现力语音合成之旅 在虚拟主播的直播间里,一句“太开心了!”如果只是平平无奇地念出来,观众很难被感染;而在智能助手中,当用户情绪低落时,机械冷漠的回应只…

作者头像 李华
网站建设 2026/3/1 10:17:57

免费视频增强神器:3步将模糊视频升级4K超清画质

免费视频增强神器:3步将模糊视频升级4K超清画质 【免费下载链接】SeedVR-7B 项目地址: https://ai.gitcode.com/hf_mirrors/ByteDance-Seed/SeedVR-7B 想要让那些模糊的家庭录像、珍贵回忆重获新生吗?字节跳动SeedVR视频增强工具为你带来专业级的…

作者头像 李华
网站建设 2026/3/3 1:16:55

dp 总结 1

shout out to professor Adzlpxsn.upd at oct 16th 2025, 修复了时间复杂度分析的重大失误.基本的, 状态, 转移, 方程状态一句话概况即为当前的属性.比如说, 贝贝现在是 3030 岁, 发了 00 张专辑, 我们就可以说 �300f 30​0.这里我们说 3030 和 00 是不同的信息, 所…

作者头像 李华
网站建设 2026/3/3 11:28:22

5大核心参数精准调优:从理论到实践的Faiss HNSW索引优化指南

5大核心参数精准调优:从理论到实践的Faiss HNSW索引优化指南 【免费下载链接】faiss A library for efficient similarity search and clustering of dense vectors. 项目地址: https://gitcode.com/GitHub_Trending/fa/faiss 面对海量向量数据的检索挑战&am…

作者头像 李华
网站建设 2026/3/5 11:18:44

LeetCode 最小覆盖子串:滑动窗口 + 哈希表高效解法

引言:为什么这道题是算法面试高频题?“最小覆盖子串”(LeetCode 76)是字符串处理领域的经典难题,也是大厂面试中高频出现的算法题。它的核心考点是滑动窗口(双指针) 与哈希表的结合运用&#xf…

作者头像 李华