并发编程--互斥锁与读写锁
并发编程–互斥锁与读写锁
文章目录
- 并发编程--互斥锁与读写锁
- 1. 基本概念
- 2. 互斥锁
- 2.1 基本逻辑
- 2.2 函数接口
- 2.3示例代码1
- 2.4示例代码2
- 3. 读写锁
- 3.1 基本逻辑
- 3.2示例代码
1. 基本概念
互斥与同步是最基本的逻辑概念:
- 互斥指的是控制两个进度使之互相排斥,不同时运行。
- 同步指的是控制两个进度使之有先有后,次序可控。

2. 互斥锁
2.1 基本逻辑
使得多线程间互斥运行的最简单办法,就是增加一个互斥锁。任何一条线成要开始运行互斥区间的代码,都必须先获取互斥锁,而互斥锁的本质是一个二值信号量,因此当其中一条线程抢先获取了互斥锁之后,其余线程就无法再次获取了,效果相当于给相关的资源加了把锁,直到使用者主动解锁,其余线程方可有机会获取这把锁。

2.2 函数接口
定义
互斥锁是一个特殊的变量,定义如下:
#include <pthread>
pthread_mutex_t m;
一般而言,由于互斥锁需要被多条线程使用,因此一般会将互斥锁定义为全局变量。
初始化与销毁
未经初始化的互斥锁是无法使用的,初始化互斥锁有两种办法:
- 静态初始化
- 动态初始化
静态初始化很简单,就是在定义同时赋予其初值:
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
由于静态初始化互斥锁不涉及动态内存,因此无需显式释放互斥锁资源,互斥锁将会伴随程序一直存在,直到程序退出为止。而所谓动态初始化指的是使用 pthread_mutex_init() 给互斥锁分配动态内存并赋予初始值,因此这种情形下的互斥锁需要在用完之后显式地进行释放资源,接口如下:
#include <pthread.h>// 初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);// 销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
接口说明:
- mutex:互斥锁
- attr:互斥锁属性(一般设置为NULL)
加锁与解锁
互斥锁的基本操作就是加锁与解锁,接口如下:
#include <pthread.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;// 加锁
pthread_mutex_lock( &m );// 解锁
pthread_mutex_unlock( &m );
2.3示例代码1
将此前判断偶数的代码用互斥锁加以改进如下:
// concurrency.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <pthread.h>pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;int global = 100;void *isPrime(void *arg)
{while(1){pthread_mutex_lock(&m);// 一段朴素的代码if(global%2 == 0)printf("%d是偶数\n", global);pthread_mutex_unlock(&m);}
}int main()
{pthread_t tid;pthread_create(&tid, NULL, isPrime, NULL);// 一条人畜无害的赋值语句while(1){pthread_mutex_lock(&m);global = rand() % 5000;pthread_mutex_unlock(&m);}
}
运行结果如下:
gec@ubuntu:~$ ./concurrency
492是偶数
2362是偶数
2778是偶数
3926是偶数
540是偶数
3426是偶数
4172是偶数
112是偶数
368是偶数
2576是偶数
1530是偶数
1530是偶数
2862是偶数
4706是偶数
...
gec@ubuntu:~$
可见,有了互斥锁之后,输出的结果正确了。
2.4示例代码2
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>pthread_mutex_t m;void *routine(void *arg)
{char *msg = (char *)arg;#ifdef MUTEXpthread_mutex_lock(&m);#endifwhile(*msg != '\0'){fprintf(stderr,"%c",*msg);usleep(100);msg++;}#ifdef MUTEXpthread_mutex_unlock(&m);#endifpthread_exit(NULL);
}int main(void)
{pthread_mutex_init(&m,NULL);pthread_t t1,t2;pthread_create(&t1,NULL,routine,"AAAAAAAAAAA");pthread_create(&t2,NULL,routine,"BBBBBBBBBBB");pthread_exit(NULL);
}
通过宏定义实现代码的不同运行,输出不同的结果。若不使用互斥锁的话,则直接运行,结果将会是AB交互是输出,两个线程t1,t2会同时运行,交互式输出;若使用互斥锁的话,会输出单独输出一个线程的结果,然后再输出另一个线程的结果。
若要使用互斥锁则如下:
gcc pthread_mutex.c -o pthread_mutex -lpthread -DMUTEX
3. 读写锁
3.1 基本逻辑
对于互斥锁而言,凡是涉及临界资源的访问一律加锁,这在并发读操作的场景下会大量浪费时间。要想提高访问效率,就必须要将对资源的读写操作加以区分:读操作可以多任务并发执行,只有写操作才进行恰当的互斥。这就是读写锁的设计来源。

读写锁提高了资源访问的效率
定义
与互斥锁类似,读写锁也是一种特殊的变量:
pthread_rwlock_t rw;
初始化
与互斥锁类似,读写锁也分成静态初始化和动态初始化:
#include <pthread.h>// 静态初始化:
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;// 动态初始化与销毁:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
加锁
读写锁最大的特点是对即将要做的读写操作做了区分:
- 读操作可以共享,因此多条线程可以对同一个读写锁加多重读锁
- 写操作天然互斥,因此多条线程只能有一个拥有写锁。(注意写锁与读锁也是互斥的)
#include <pthread.h>// 读锁
// 1,阻塞版本
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
// 2,非阻塞版本
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);// 写锁
// 1,阻塞版本
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
// 2,非阻塞版本
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
操作原则:
- 如果只对数据进行读操作,那么就加 → 读锁。
- 如果要对数据进行写操作,那么就加 → 写锁。
解锁
#include <pthread.h>int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
3.2示例代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>char global = 'X';pthread_rwlock_t rwlock;void *routine(void *arg)
{#ifdef RDLOCKpthread_rwlock_rdlock(&rwlock);#elif WRLOCKpthread_rwlock_wrlock(&rwlock);#endifint i = 1000;while(i > 0){fprintf(stderr,"[%c:%c]",*(char*)arg,global);i--;}pthread_rwlock_unlock(&rwlock);pthread_exit(NULL);
}int main(void)
{pthread_rwlock_init(&rwlock,NULL);pthread_t t1,t2,t3;pthread_create(&t1,NULL,routine,"1");pthread_create(&t2,NULL,routine,"2");pthread_create(&t3,NULL,routine,"3");pthread_exit(NULL);
}
相关文章:
并发编程--互斥锁与读写锁
并发编程–互斥锁与读写锁 文章目录 并发编程--互斥锁与读写锁1. 基本概念2. 互斥锁2.1 基本逻辑2.2 函数接口2.3示例代码12.4示例代码2 3. 读写锁3.1 基本逻辑3.2示例代码 1. 基本概念 互斥与同步是最基本的逻辑概念: 互斥指的是控制两个进度使之互相排斥&#x…...
记录第一次使用H5的WebBluetooth完成蓝牙标签打印机的(踩坑)过程
第1步 首先第一步,调试环境必须是https的,由于浏览器的强制安全策略,本地可以采用localhost 第2步 然后,如果打印需要服务UUID(Service UUID) 和 特征UUID(Characteristic UUID)&…...
2025 年“认证杯”数学中国数学建模网络挑战赛 A题 小行星轨迹预测
近地小行星( Near Earth Asteroids, NEAs )是轨道相对接近地球的小行 星,它的正式定义为椭圆轨道的近日距不大于 1.3 天文单位( AU )的小行星。 其中轨道与地球轨道最近距离小于 0.05A 且直径大于 140 米的小行星被…...
【WRF理论第十七期】单向/双向嵌套机制(含namelist.input详细介绍)
WRF运行的单向/双向嵌套机制 准备工作:WRF运行的基本流程namelist.input的详细设置&time_control 设置&domain 嵌套结构&bdy_control 配置部分 namelist 其他注意事项Registry.EM 运行 ARW 嵌套双向嵌套(two-way nesting)Moving …...
Spring Boot 3.4.3 和 Spring Security 6.4.2 结合 JWT 实现用户登录
在现代 Web 应用中,用户认证和授权是保障系统安全的核心环节。传统的 Session 认证方式在分布式系统或前后端分离场景下存在局限,而 JWT(JSON Web Token)作为一种无状态的认证机制,凭借其轻量、可扩展和跨服务的特性&a…...
Python 实现的运筹优化系统数学建模详解(0-1规划指派问题)
一、引言 在数学建模的广阔领域中,指派问题作为一类经典且重要的组合优化问题,频繁出现在各类实际场景里。例如,在人力资源管理中,如何将不同技能水平的员工高效地分配到各个项目,以实现项目成本最小化或收益最大化&am…...
TCP转发隧道
✅ 功能特性: 1. 高并发支持:采用 threading.Thread socket,可承载多并发连接 2. 异常处理完善:确保线程内异常不会崩溃整个程序 3. 可持续运行:守护线程 主线程监控机制 4. 运行状态监控: • 当前活跃连…...
React 学习 JSX
APP根组件被index.js渲染到public下的index.html下 JS中写 HTML 代码 渲染列表 条件渲染 复杂条件渲染 事件绑定 传递自定义参数 button标签中写箭头函数引用的格式 自定义参数和事件本身对象都想要的情况...
大模型论文:Language Models are Few-Shot Learners(GPT3)
大模型论文:Language Models are Few-Shot Learners(GPT3) 文章地址:https://proceedings.neurips.cc/paper_files/paper/2020/file/1457c0d6bfcb4967418bfb8ac142f64a-Paper.pdf 一、摘要 我们证明了,扩大语言模型的规模在任务无关的 few…...
一周学会Pandas2 Python数据处理与分析-Pandas2数据导出
锋哥原创的Pandas2 Python数据处理与分析 视频教程: 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 任何原始格式的数据载入DataFrame后,都可以使用类似 DataFrame.to_csv()的方法输出到相应格式的文件或者…...
深入解析栈式虚拟机与反向波兰表示法
1.1 什么是虚拟机? 虚拟机(Virtual Machine, VM)是一种软件实现的计算机系统,提供与物理计算机相类似的环境,但在软件层面运行。虚拟机的存在简化了跨平台兼容性、资源管理以及安全隔离等问题。 1.2 栈式虚拟机的架构…...
python中的数据模型-pydantic浅讲
数据模型-pydantic 1. 基本用法 通过继承 BaseModel,你可以定义一个数据模型类。类的属性使用类型注解来声明字段的类型 from pydantic import BaseModelclass User(BaseModel):name: strage: intis_active: bool True # 默认值字段类型:每个字段…...
15.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--如何拆分单体
单体应用(Monolithic Application)是指将所有功能模块集中在一个代码库中构建的应用程序。它通常是一个完整的、不可分割的整体,所有模块共享相同的运行环境和数据库。这种架构开发初期较为简单,部署也较为方便,但随着…...
华为数字芯片机考2025合集4已校正
单选 1. 题目内容 影响芯片成本的主要因素是 Die Size 和封装,但电源、时钟等因素,特别是功耗对解决方案的成本影响较大,因此低成本设计需要兼顾低功耗设计:() 1. 解题步骤 1.1 分析题目 Die Size&…...
Java面试黄金宝典47
1. 如何设计一个秒杀系统 定义 秒杀系统是一种应对短时间内大量用户并发请求的系统,其核心目标是在高并发场景下保证系统的稳定性、数据的一致性,避免超卖等问题,同时快速响应用户请求。 秒杀系统设计需从多个层面考虑,以应对高并发场景: 前端优化: 页面静态化:将商品详…...
学习MySQL的第八天
海到无边天作岸 山登绝顶我为峰 一、数据库的创建、修改与删除 1.1 引言 在经过前面七天对于MySQL基本知识的学习之后,现在我们从基本的语句命令开始进入综合性的语句的编写来实现特定的需求,从这里开始需要我们有一个宏观的思想&…...
AI识别与雾炮联动:工地尘雾治理新途径
利用视觉分析的AI识别用于设备联动雾炮方案 背景 在建筑工地场景中,人工操作、机械作业以及环境因素常常导致局部出现大量尘雾。传统监管方式存在诸多弊端,如效率低、资源分散、监控功能单一、人力效率低等,难以完美适配现代工程需求。例如…...
GD32F303-IAP的过程和实验
使用的芯片为GD32F303VC 什么是IAP呢?有个博主写的很清楚;就是远程升级; 【单片机开发】单片机的烧录方式详解(ICP、IAP、ISP)_isp烧录-CSDN博客 我们需要写一个boot 和APP 通过 boot对APP的程序进行更新…...
众趣科技助力商家“以真示人”,让消费场景更真实透明
在当今的消费环境中,消费者权益保护问题日益凸显。无论是网购商品与实物不符、预定酒店民宿与图文描述差异大,还是游览景区遭遇“照骗”,这些问题不仅让消费者在消费和决策过程中倍感困扰,也让商家面临信任危机。 消费者在享受便…...
spark core编程之行动算子、累加器、广播变量
一、RDD 行动算子 reduce:聚集 RDD 所有元素,先聚合分区内数据,再聚合分区间数据。 collect:在驱动程序中以数组形式返回数据集所有元素。 foreach:分布式遍历 RDD 元素并调用指定函数。 count:返回 RDD…...
提高课:数据结构之树状数组
1,楼兰图腾 #include<iostream> #include<cstring> #include<cstdio> #include<algorithm>using namespace std;typedef long long LL;const int N 200010;int n; int a[N]; int tr[N]; int Greater[N], lower[N];int lowbit(int x) {ret…...
基于javaweb的SpringBoot新闻视频发布推荐评论系统(源码+部署文档)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…...
使用Go语言实现自动清理应用系统日志
在服务器上部署业务应用系统,每天都会产生大量的日志,随着时间的推移,日志越积累越多,占用了大量的磁盘空间,除了可以手动清理日志外,还可以通过程序实现自动清理日志。 之所以选择Go语言,是因…...
机器学习之PCA主成分分析详解
文章目录 引言一、PCA的概念二、PCA的基本数学原理2.1 内积与投影2.2 基2.3 基变换2.4 关键问题及优化目标2.5 方差2.6 协方差2.7 协方差矩阵2.8 协方差矩阵对角化 三、PCA执行步骤总结四、PCA计算实例五、PCA参数解释六、代码实现七、PCA的优缺点八、总结 引言 在机器学习领域…...
回溯——固定套路 | 面试算法12道
目录 输出二叉树所有路径 路径总和问题 组合总和问题 分割回文串 子集问题 排列问题 字母大小写全排列 单词搜索 复原IP地址 电话号码问题 括号生成问题 给我一种感觉是回溯需要画图思考是否需要剪枝。 元素个数n相当于树的宽度(横向)&#x…...
【11】Strongswan processor 详解1
processor_t结构体,声明了一些公用方法: get_total_threads获取总的线程数量; get_idle_threads获取空闲线程数量; get_working_threads按指定的优先级获取处理该优先级的job的线程数量; get_job_load 或取指定优先级j…...
Maven和MyBatis学习总结
目录 Maven 1.Maven的概念: 2.在具体的使用中意义: 3.与传统项目引入jar包做对比: 传统方式: 在maven项目当中: 4.在创建maven项目后,想要自定义一些maven配置 5.maven项目的结构 6.maven指令的生…...
普通通话CSFB方式(2g/3g)
一、CSFB的触发条件 当模块(或手机)驻留在 4G LTE网络 时,若发生以下事件,会触发CSFB流程: 主叫场景:用户主动拨打电话。被叫场景:接收到来电(MT Call)。紧急呼叫&…...
揭开人工智能与机器学习的神秘面纱:开发者的视角
李升伟 编译 人工智能(AI)和机器学习(ML)早已不再是空洞的流行语——它们正在彻底改变我们构建软件、做出决策以及与技术互动的方式。无论是自动化重复性任务,还是驱动自动驾驶汽车,AI/ML都是现代创新的核…...
AndroidTV 当贝播放器-v1.5.2-官方简洁无广告版
AndroidTV 当贝播放器 链接:https://pan.xunlei.com/s/VONXRf0g3cT0ECVt6GEsoODFA1?pwds4qv# AndroidTV 当贝播放器-v1.5.2-官方简洁无广告版...
