C++并发及互斥保护示例

news2025/1/18 17:07:36

        最近要写一个多线程的并发数据库,主要是希望使用读写锁实现库的并发访问,同时考虑到其他平台(如Iar)没有C++的读写锁,需要操作系统提供,就将读写锁封装起来。整个过程还是比较曲折的,碰到了不少问题,在此就简单分析总结下并发和互斥吧。

        首先,先贴上一部分源代码:

#include <shared_mutex>
#include <iostream>
#include <windows.h>
#include <synchapi.h>

using cegn_mutex = std::shared_mutex;
cegn_mutex g_cegn_mutex;
void cegn_mutex_unique_lck(cegn_mutex& testmutex)	//独占锁,写数据
{
	std::unique_lock<cegn_mutex> cegn_lock(testmutex);
}

void cegn_mutex_share_lck(cegn_mutex& Dbmutex)	//共享锁,读数据
{
	std::shared_lock<cegn_mutex> cegn_lock(Dbmutex);
}

void cegn_mutex_unlck(cegn_mutex& Dbmutex)
{
	;	//vc读写锁离开作用域自动释放
}

int g_dwVal = 0;
void FastWriteData(int i)
{
	while (1)
	{
		cegn_mutex_unique_lck(g_cegn_mutex);
		g_dwVal++;
		std::cout << "FastWriteData " << "  Set dwVal= " << g_dwVal << "\n";

		Sleep(1000);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

void SlowWriteData(int i)
{
	while (1)
	{
		cegn_mutex_unique_lck(g_cegn_mutex);
		g_dwVal++;
		std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
		Sleep(5000);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

void ReadData(int i)
{
	while (1)
	{
		cegn_mutex_share_lck(g_cegn_mutex);
		std::cout << "ReadData " << " Get dwVal= " << g_dwVal << "\n";
		Sleep(500);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

int main()
{
	std::cout << "main start !!" << std::endl;

	std::thread thread1 = std::thread(FastWriteData, 0);
	std::thread thread2 = std::thread(SlowWriteData, 0);
	thread1.join();
	thread2.join();

	getchar();
	return 1;
}

代码不长,逻辑也挺清晰的,但结果不正确:

似乎就没有互斥保护,因为FastWriteData和SlowWriteData中都独占了cegn_mutex_unique_lck(g_cegn_mutex);

且在while(1)中,不存在释放写锁的情况,那就不应该两个写线程交替出现。

如上让chatgpt分析下,它认为没啥问题,我尝试修改回标准读写锁接口,如下:

void FastWriteData(int i)
{
	while (1)
	{
//		cegn_mutex_unique_lck(g_cegn_mutex);
		std::unique_lock<cegn_mutex> lck(g_cegn_mutex);
		g_dwVal++;
		std::cout << "FastWriteData " << "  Set dwVal= " << g_dwVal << "\n";

		Sleep(1000);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

void SlowWriteData(int i)
{
	while (1)
	{
//		cegn_mutex_unique_lck(g_cegn_mutex);
		std::unique_lock<cegn_mutex> lck(g_cegn_mutex);
		g_dwVal++;
		std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
		Sleep(5000);
		cegn_mutex_unlck(g_cegn_mutex);
	}
}

 如上,代码运行就是正常了

main start !!
FastWriteData   Set dwVal= 1
FastWriteData   Set dwVal= 2
FastWriteData   Set dwVal= 3
FastWriteData   Set dwVal= 4
FastWriteData   Set dwVal= 5
FastWriteData   Set dwVal= 6
FastWriteData   Set dwVal= 7
FastWriteData   Set dwVal= 8
FastWriteData   Set dwVal= 9
FastWriteData   Set dwVal= 10
FastWriteData   Set dwVal= 11
FastWriteData   Set dwVal= 12
FastWriteData   Set dwVal= 13
FastWriteData   Set dwVal= 14

现在FastWriteData就独占了互斥量,导致SlowWriteData无法运行。为啥使用接口:

void cegn_mutex_unique_lck(cegn_mutex& testmutex)    //独占锁,写数据
{
    std::unique_lock<cegn_mutex> cegn_lock(testmutex);
}

就不行了?

修改成直接调用:

using cegn_mutex = std::shared_mutex;
cegn_mutex g_cegn_mutex;
void cegn_mutex_unique_lck(cegn_mutex& testmutex)	//独占锁,写数据
{
//	std::unique_lock<cegn_mutex> cegn_lock(testmutex);
	std::unique_lock<cegn_mutex> cegn_lock(g_cegn_mutex);
}

还是不能正确互斥,修改如下也一样:

void cegn_mutex_unique_lck(cegn_mutex& testmutex)	//独占锁,写数据
{
//	std::unique_lock<cegn_mutex> cegn_lock(testmutex);
	std::unique_lock<std::shared_mutex> cegn_lock(g_cegn_mutex);
}

经过分析,问题是:

void cegn_mutex_unique_lck(cegn_mutex& testmutex)

函数中定义了一个互斥量cegn_lock :

std::unique_lock<cegn_mutex> cegn_lock(testmutex);

该互斥量在函数退出的时候,生命周期就结束了,所以自动销毁,最终导致无法互斥,那是在想要封装,如何实现呢,可以自己协议个类封装:

完整的简单代码如下:

#include <iostream>
#include <thread>
#include <mutex>
#include <windows.h>

class MutexWrapper {
public:
	MutexWrapper(std::mutex& mutex) : m_mutex(mutex) {
		m_mutex.lock();
	}

	~MutexWrapper() {
		m_mutex.unlock();
	}

private:
	std::mutex& m_mutex;
};

std::mutex g_mutex_test;
int g_dwVal = 0;

void FastWriteData(int i) {
	while (1) {
		MutexWrapper lock(g_mutex_test);
		g_dwVal++;
		std::cout << "FastWriteData " << "  Set dwVal= " << g_dwVal << "\n";
		Sleep(1000);
	}
}

void SlowWriteData(int i) {
	while (1) {
		MutexWrapper lock(g_mutex_test);
		g_dwVal++;
		std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
		Sleep(3000);
	}
}

int main() {
	std::cout << "main start !!" << std::endl;

	std::thread thread1 = std::thread(FastWriteData, 0);
	std::thread thread2 = std::thread(SlowWriteData, 0);
	thread1.join();
	thread2.join();

	getchar();
	return 1;
}

如此,运行正常了

修改下例程,让两个进程都整行跑 

void FastWriteData(int i) {
	while (1) {
		{
			MutexWrapper lock(g_mutex_test);
			g_dwVal++;
			std::cout << "FastWriteData " << "  Set dwVal= " << g_dwVal << "\n";
		}
		Sleep(1000);
	}
}

void SlowWriteData(int i) {
	while (1) {
		{
			MutexWrapper lock(g_mutex_test);
			g_dwVal++;
			std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
		}
		Sleep(3000);
	}
}

如上,代码就基本都正常了。

当然,也可以将互斥锁修改为读写锁,如下:

class MutexWrapper {
public:
	MutexWrapper(std::shared_mutex& mutex) : m_mutex(mutex) {
		m_mutex.lock();
	}

	~MutexWrapper() {
		m_mutex.unlock();
	}

private:
	std::shared_mutex& m_mutex;
};

std::shared_mutex g_mutex_test;

代码也运行正常了。

综上:

1:基于RAII,C++的很多变量生命周期有限,必须特别注意智能变量的生命周期。

2:如果需要封装读写锁,不能简单函数分装,实在不行,就用一个类封装吧

3:要熟练掌握std::thread,std::shared_mutex,std::mutex的用法,这个是变法互斥基本要求

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iOS中一种超简单的路由实现方式

看了很多的路由实现方式&#xff0c;发现实现的都太复杂&#xff0c;越是复杂的东西越是难以维护&#xff0c;当然复杂的东西好处就是覆盖面比较全。而本文则是使用一种比较简单的方式实现路由的&#xff0c;当然如有建议或者想法可以及时跟我沟通&#xff0c;让我也能有更好的…

reeds_sheep运动规划算法Python源码分析

本文用于记录Python版本zhm-real / PathPlanning运动规划库中reeds_sheep算法的源码分析 关于reeds sheep算法的原理介绍前文已经介绍过了&#xff0c;链接如下所示&#xff1a; 《Reeds-Shepp曲线学习笔记及相关思考》 《Reeds-Shepp曲线基础运动公式推导过程》 正文&#xff…

vite初始化vue3项目(配置自动格式化工具与git提交规范工具)

初始化项目 vite构建vue项目还是比较简单的&#xff0c;简单配置选择一下就行了 初始化命令 npm init vuelatest初始化最新版本vue项目 2. 基本选项含义 Add TypeScript 是否添加TSADD JSX是否支持JSXADD Vue Router是否添加Vue Router路由管理工具ADD Pinia 是否添加pinia…

直方图均衡化和自适应直方图均衡化

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 均衡化是数字图像处理中常用的一种技术&#xff0c;用于增强图像的视觉效果和对比度。&#xff0c;今天我们将实现对同一张图像的直方图均衡化和自适应直方图均衡化处理&#xff0c;学习一下两者的的基本原理和实现过程&a…

docker搭建redis主从复制

docker安装redis docker pull redis:latest创建目录结构 用于挂载数据卷配置文件等 运行redis容器 主redis配置 下面这种方式挂载的配置文件不生效。 docker run -d \--name master_redis_6379 \-p 6379:6379 \--restart unless-stopped \-v /mydata/master_redis/data:/da…

集简云本周新增/更新:新增3大功能,集成19款应用,更新5款应用,新增近290个动作

本周更新概要 功能更新 ◉ 新增功能&#xff1a;语聚AI开放API功能 ◉ 新增功能&#xff1a;数据表表格公开分享功能 ◉ 新增功能&#xff1a;浏览器页面操作页面内容读取(增强版本&#xff09; 应用新增 新增应用&#xff1a;赛捷CRM 新增应用&#xff1a;快跑者 新增应…

LeGO-Loam代码解析(一) 项目介绍、论文解读、配置安装

目录 1.项目介绍 2. 论文解读 LeGO-LOAM&#xff1a;轻量级且地面优化的可变地形激光里程计与建图 2.1 摘要 2.2 介绍 2.3 正文部分1 --- System Review 2.4 正文部分2 --- Segmatation&#xff08;地面点角面点分离&#xff09; 2.5 Feature Extraction 正文部分3 ---…

QT Quick之quick与C++混合编程

Qt quick能够生成非常绚丽界面&#xff0c;但有其局限性的&#xff0c;对于一些业务逻辑和复杂算法&#xff0c;比如低阶的网络编程如 QTcpSocket &#xff0c;多线程&#xff0c;又如 XML 文档处理类库 QXmlStreamReader / QXmlStreamWriter 等等&#xff0c;在 QML 中要么不可…

【操作系统考点汇集】操作系统考点汇集

关于操作系统可能考察的知识点 操作系统基本原理 什么是操作系统&#xff1f; 操作系统是指控制和管理整个计算机系统的硬件和软件资源&#xff0c;并合理地组织调度计算机的工作和资源的分配&#xff0c;以提供给用户和它软件方便的接口和环境&#xff0c;是计算机系统中最基…

【C语言学习】参数传递

调用函数 1.如果函数有参数&#xff0c;调用函数时必须传递给它数量、类型正确的的值。 2.可以传递给函数的值是表达式的结果&#xff0c;包括&#xff1a; 字面量 变量 函数的返回值 计算的结果 int a,b,c; a5; b6; cmax(10,12); cmax(a,b); cmax(c,23); cmax(max(23,45),a);…

opencv实战项目-停车位计数

手势识别系列文章目录 手势识别是一种人机交互技术&#xff0c;通过识别人的手势动作&#xff0c;从而实现对计算机、智能手机、智能电视等设备的操作和控制。 1. opencv实现手部追踪&#xff08;定位手部关键点&#xff09; 2.opencv实战项目 实现手势跟踪并返回位置信息&a…

前端基础(JavaScript语法)

前言&#xff1a;今天学习JavaScript的基本语法&#xff0c;包括变量、函数、对象、数组。 目录 JavaScript 变量 函数 对象 数组 JavaScript 变量 定义变量 判断语句 判断等于&#xff1a; 判断不等于&#xff1a;! if else语句 if(vavb){ console.log("…

Harmony OS教程学习笔记

基础知识 1.如何修改程序启动的第一个页面&#xff1f; 不想使用创建的默认的页面&#xff0c;这时需要修改启动页面&#xff0c;修改的地方在EntryAbility文件中的onWindowStageCreate方法中。 onWindowStageCreate(windowStage: window.WindowStage) {// Main window is cr…

解决无法访问 Github 问题

GitHub作为程序员访问最频繁的网站&#xff0c;程序员们经常需要访问 Github找开源项目、学习新框架、管理自己的个人开源项目等等。 github加速器 因为GitHub属于国外的网站&#xff0c;直接访问的话&#xff0c;速度非常慢&#xff0c;甚至访问不了&#xff0c; 今天给大家…

centos下使用jemalloc解决Mysql内存泄漏问题

参考&#xff1a; MySQL bug&#xff1a;https://bugs.mysql.com/bug.php?id83047&tdsourcetags_pcqq_aiomsg https://github.com/jemalloc/jemalloc/blob/dev/INSTALL.md &#xff08;1&#xff09;ptmalloc 是glibc的内存分配管理 &#xff08;2&#xff09;tcmalloc…

如何批量修改图片名为不同名称

如何批量修改图片名为不同名称&#xff1f;当今社会&#xff0c;因为人们都养成了随手拍照的习惯&#xff0c;所以拥有上千上万张照片的相册已经司空见惯不足为奇。然而&#xff0c;我们在保存这些照片时往往都会碰到一个大难题——电脑中的图片名称千奇百怪&#xff0c;让整个…

打开软件提示mfc100u.dll缺失是什么意思?要怎么处理?

当你打开某个软件或者运行游戏&#xff0c;系统提示mfc100u.dll丢失&#xff0c;此时这个软件或者游戏根本无法运行。其实&#xff0c;mfc100u.dll是动态库文件&#xff0c;它是VS2010编译的软件所产生的&#xff0c;如果电脑运行程序时提示缺少mfc100u.dll文件&#xff0c;程序…

由“美”出发 听艺术家林曦关于美育与智慧的探讨

不久前&#xff0c;林曦老师与我们的老朋友「十点读书」进行了一次线上直播&#xff0c;有关林曦老师十余年的书法教学&#xff0c;和传统美育的心得&#xff0c;以及因此诞生的新书《无用之美》。      这一次的直播&#xff0c;由“美”的主题出发&#xff0c;延伸出美育…

微服务参数透传实现

说明&#xff1a;在微服务架构中&#xff0c;用户身份经网关验证后&#xff0c;我们可以将用户信息&#xff0c;如ID加入到请求头上。后面的微服务中&#xff0c;可以设置一个拦截器&#xff0c;拦截请求&#xff0c;获取请求头上的用户ID&#xff0c;加入到ThreadLocal中。 最…

Spring【学习记录一】

Spring内容解释 早期的Spring仅指代Spring Framework&#xff0c;后来基于Spring Framework孵化出大量的项目&#xff0c;Spring的含义变成了指代Spring家族 Spring Framework是Spring家族所有成员的基础&#xff0c;想要学透Spring&#xff0c;就必须要掌握Spring Framework…