Linux系统 | 线程的同步与互斥

💓个人主页:mooridy
💓专栏地址:Linux
关注我🌹,和我一起学习更多计算机的知识!
🔝🔝🔝

前置知识

  • 临界资源:多线程执行流共享的资源就叫做临界资源
  • 临界区:每个线程内部,访问有临界资源的代码,就叫做临界区
  • 原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成

线程互斥

什么是互斥?

互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用。

为啥需要互斥?
  • ⼤部分情况,线程使⽤的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程⽆法获得这种变量。

  • 但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互。

多个线程并发的操作共享变量,可能会出现一些问题,比如下面这种情况。
![[Pasted image 20250328201239.png]]
像i++这样的操作并不是原子的,它的底层汇编代码有三句,即上图红色的三步。如果在这中间发生时钟中断,就会导致结果异常。
要解决这种问题,我们需要保证几个条件:

  • 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
  • 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
  • 如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。

要实现这些条件,我们就需要一把,Linux上我们把这把锁叫做互斥量
![[Pasted image 20250328202101.png]]

互斥量的接口
初始化互斥量
方法 1:静态分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  • 解释:这种方式是在编译时对互斥量进行初始化,它是一种简单直接的初始化方式。PTHREAD_MUTEX_INITIALIZER 是一个宏,它为互斥量提供了默认的初始值。使用这种方式初始化的互斥量,其生命周期与包含它的变量相同,并且不需要手动调用销毁函数来释放资源。这种方式适合那些在程序运行期间一直存在且不需要动态调整属性的互斥量。
方法 2:动态分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
  • 参数解释
    • mutex:这是一个指向 pthread_mutex_t 类型的指针,代表要初始化的互斥量。在调用该函数时,需要提供一个有效的 pthread_mutex_t 变量的地址。
    • attr:这是一个指向 pthread_mutexattr_t 类型的指针,通常将其设置为 NULL。当设置为 NULL 时,表示使用默认的互斥量属性。
  • 返回值
    • 若初始化成功,函数返回 0
    • 若出现错误,函数会返回一个非零的错误码。
销毁互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);
注意事项
  • 静态初始化的互斥量:用 PTHREAD_MUTEX_INITIALIZER 初始化的互斥量不需要销毁,因为它是静态分配的,其资源会在程序结束时自动释放。
返回值
  • 若销毁成功,函数返回 0
  • 若出现错误,函数会返回一个非零的错误码。
互斥量加锁和解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值
  • 成功返回 0
  • 失败返回错误号。
pthread_mutex_lock 调用情况分析
  • 互斥量未锁状态:当互斥量处于未锁状态时,pthread_mutex_lock 函数会将互斥量锁定,同时返回成功。这意味着当前线程成功获得了对共享资源的访问权,其他线程在该互斥量解锁之前无法获得访问权。
  • 互斥量已锁状态:当发起函数调用时,如果其他线程已经锁定了互斥量,或者存在其他线程同时申请互斥量,但当前线程没有竞争到互斥量,那么 pthread_mutex_lock 调用会陷入阻塞(执行流被挂起)。此时,当前线程会暂停执行,直到互斥量被解锁,然后再次尝试获取互斥量。

线程同步

什么是同步?
  • 所谓同步,在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题
    e.g.「操作 A 应在操作 B 之前执行」,「操作 C 必须在操作 A 和操作 B 都完成之后才能执行」
如何实现同步?

线程同步需要由条件变量来实现。
条件变量是一种机制。当线程持有锁进入临界区后,如果发现资源未就绪,它可以释放锁并进入等待状态。当其他线程将资源准备好后,会通过条件变量通知等待的线程。这样等待的线程就不需要频繁地检测资源是否就绪,一旦资源就绪就会被通知来访问资源,从而避免了 CPU 资源的浪费。

条件变量函数
初始化
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

此函数用于初始化一个条件变量。

参数

  • cond:这是一个指向 pthread_cond_t 类型的指针,代表要初始化的条件变量。在调用该函数时,需提供一个有效的 pthread_cond_t 变量的地址,这样函数才能对其进行初始化操作。
  • attr:这是一个指向 pthread_condattr_t 类型的指针,通常将其设置为 NULL。当设置为 NULL 时,表示使用默认的条件变量属性。如果需要自定义条件变量的属性,可先创建一个 pthread_condattr_t 类型的变量,对其进行相应的设置,然后将其地址传递给该参数。

返回值

  • 若初始化成功,函数返回 0
  • 若出现错误,函数会返回一个非零的错误码。
销毁
int pthread_cond_destroy(pthread_cond_t *cond);

该函数用于销毁一个已经初始化的条件变量。

参数

  • cond:这是一个指向 pthread_cond_t 类型的指针,代表要销毁的条件变量。在调用该函数时,需提供一个已经初始化的 pthread_cond_t 变量的地址。

返回值

  • 若销毁成功,函数返回 0
  • 若出现错误,函数会返回一个非零的错误码。
等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

此函数用于让当前线程在指定的条件变量上等待,直到其他线程通过 pthread_cond_signalpthread_cond_broadcast 函数唤醒它。

参数

  • cond:这是一个指向 pthread_cond_t 类型的指针,代表要在这个条件变量上等待。当前线程会在该条件变量上进入阻塞状态,直到条件满足被唤醒。
  • mutex:代表一个互斥量。在调用 pthread_cond_wait 函数之前,当前线程必须已经锁定了该互斥量。在函数内部,它会自动解锁该互斥量,并让线程进入等待状态。当线程被唤醒后,它会自动重新锁定该互斥量。

返回值

  • 若线程成功被唤醒并重新锁定互斥量,函数返回 0
  • 若出现错误,函数会返回一个非零的错误码。
唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

这两个函数用于唤醒在指定条件变量上等待的线程。

pthread_cond_broadcast 函数

该函数会唤醒所有在指定条件变量 cond 上等待的线程。

pthread_cond_signal 函数

该函数会唤醒在指定条件变量 cond 上等待的一个线程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/42366.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Dify 服务器部署指南

1. 系统要求 在开始部署之前,请确保你的服务器满足以下要求: 操作系统:Linux(推荐使用 Ubuntu 20.04 或更高版本)内存:至少 4GB RAM存储:至少 20GB 可用空间网络:稳定的互联网连接…

Sa-Token

简介 Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。 官方文档 常见功能 登录认证 本框架 用户提交 name password 参数,调用登…

ADZS-ICE-2000和AD-ICE2000仿真器在线升级固件

作者的话 近期发现有些兄弟的ICE-2000仿真器链接DSP报错,然后test第四步不通过,我就拿我的仿真器也试了一下,发现ADI悄咪咪的在线升级仿真器固件,有些兄弟不会操作,就会导致仿真器升级失败,连不上目标板&a…

C++概述

1 什么是面向对象】 概念上来说:就是以对象(具体的变量)为导向的编程思路 专注于:一个对象具体能实现哪些过程(哪些功能) 面向对象 n * 面向过程 结论:面向对象需要做的事情 1:我们要想清楚,我们现在需要编写一个…

Java 大视界 -- 基于 Java 的大数据隐私计算在医疗影像数据共享中的实践探索(158)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…

数字化如何赋能食品抽检全流程升级,助力食品安全监管现代化

食品安全是关乎民众健康和社会稳定的重要问题。食品抽检作为保障食品安全的核心监管手段,通过对食品生产、加工、销售等环节的随机抽样检测,及时发现潜在的食品安全问题,防止不合格产品流入市场,同时为政府监管、企业自查和消费者…

HBase入门教程

HBase入门教程 HBase是一个开源的、分布式的、版本化的非关系型数据库,是Apache Hadoop生态系统的重要组成部分。本文将全面介绍HBase的基础知识,帮助你快速入门。 文章目录 HBase入门教程1. HBase简介1.1 什么是HBase?1.2 HBase核心特点 2.…

vscode连接服务器失败问题解决

文章目录 问题描述原因分析解决方法彻底删除VS Code重新安装较老的版本 问题描述 vscode链接服务器时提示了下面问题: 原因分析 这是说明VScode版本太高了。 https://code.visualstudio.com/docs/remote/faq#_can-i-run-vs-code-server-on-older-linux-distribu…

redis常用部署架构之redis分片集群。

redis 3.x版本后开始支持 作用: 1.提升数据读写速度 2..提升可用性 分片集群就是将业务服务器产生的数据储存在不同的机器上。 redis分片集群的架构 如上图所示,会将数据分散存储到不同的服务器上,相比于之前来说,redis要处…

Modbus主站EtherNet/IP转ModbusRTU/ASCII工业EIP网关串口服务器

型号 2路总线EIP网关 MS-A1-2021 4路总线EIP网关 MS-A1-2041 4路总线EIP网关(双网口) MS-A2-2041 8路总线EIP网关 MS-A1-2081 8路总线EIP网关(双网口) MS-A2-2081 EtherNet/IP 串口网关 EtherNet/IP 转 RS485 …

Centos7 安装 TDengine

Centos7 安装 TDengine 1、简介 官网: https://www.taosdata.com TDengine 是一款开源、高性能、云原生的时序数据库(Time Series Database, TSDB), 它专为物联网、车联网、工业互联网、金融、IT 运维等场景优化设计。同时它还带有内建的缓…

基于社交裂变的S2B2C电商模式创新研究——以“颜值PK+礼品卡+AI智能名片“融合生态为例

摘要 本文构建了融合开源AI技术、社交裂变机制与S2B2C商业模式的创新模型。通过开发具备AI智能名片功能的商城小程序,实现用户日均停留时长提升171%、社交转化效率提高2.8倍的实证效果。研究发现:基于GAN的虚拟形象生成技术可降低用户决策成本32%&…

王者荣耀服务器突然崩了

就在刚刚王者荣耀服务器突然崩了 #王者荣耀崩了#的话题毫无预兆地冲上热搜,许多玩家发现游戏登录界面反复弹出异常提示,匹配成功后卡在加载界面,甚至出现对局数据丢失的情况。根据官方公告,目前技术团队已在全力抢修服务器 #王者…

LabVIEW医疗设备备用电源实时监控系统

开发了一个基于LabVIEW的医疗设备备用电源实时监控系统。系统提高医疗设备备用电源的管理效能与使用安全,通过实时监测与数据分析,确保医疗设施在电力供应中断时的可靠运行。 ​ 项目背景 医院中的医疗设备对电源的连续供应有着极高的要求,…

04-SpringBoot3入门-配置文件(多环境配置)

1、简介 在 SpringBoot 中,不同的环境(如开发、测试、生产)可以编写对应的配置文件,例如数据库连接信息、日志级别、缓存配置等。在不同的环境中使用对应的配置文件。 2、配置环境 # 开发环境 zbj:user:username: root # 测试环…

C++链表详解:从基础概念到高级应用

C++链表详解:从基础概念到高级应用 链表是计算机科学中最基础也是最重要的数据结构之一,它在内存管理、算法实现和实际应用中扮演着关键角色。本文将详细介绍链表的概念、类型、C++实现以及实际应用场景,帮助读者全面理解这一重要的数据结构。 文章目录 C++链表详解:从基础…

了解图像质量评价指标PSNR

一、PSNR是什么 1.1 定义与数学公式 峰值信噪比(Peak Signal-to-Noise Ratio,PSNR)是数字图像处理领域最经典的客观质量评价指标之一。其核心思想是通过计算原始图像与失真图像之间的均方误差(MSE)来衡量失真程度&am…

NX二次开发刻字功能——布尔运算

刻字功能在经历、创建文本、拉伸功能以后就剩下布尔运算了。布尔运算的目的就是实现文本时凸还是凹。这部分内容很简单。 1、首先识别布尔运算的类型,我这里用到一个枚举类型的选项,凸就是布尔求和,凹就是布尔求差。 2、其放置位置为创建拉伸…

《C语言实现金字塔图案打印》

🚀个人主页:BabyZZの秘密日记 📖收入专栏:C语言练习题分享 🌍文章目入 程序代码程序功能程序分析外层循环内层循环输出结果 示例运行总结 在学习编程的过程中,打印图案是一个非常有趣的练习,它可…

Shiro学习(一):Shiro介绍和基本使用

一、Shiro介绍 1、百科对shiro的定义如下: Apache Shiro 一个强大且易于使用的 Java 安全框架,它提供了身份验证、授权、加密和会话管理等功能。Shiro 的设计目标是简化企业级应用程序的安全性开发过程,同时保持代码的简洁和易于维护。 2、…