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

        最近要写一个多线程的并发数据库,主要是希望使用读写锁实现库的并发访问,同时考虑到其他平台(如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.hqwc.cn/news/72382.html

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

相关文章

一文学会配置Fanuc控制柜端ROS2驱动

文章目录 前言一、RobotGuide是什么&#xff1f;二、实现步骤创建机器人工作单元导入程序TP程序Karel程序 构建程序配置控制柜配置机器人控制柜通讯配置可同时运行程序数量配置ROS_RELAY变量配置ROS_STATE变量设置启用标志 三、测试机器人状态反馈机器人命令接收整体运行测试 总…

(stm32)低功耗模式

低功耗模式 执行哪个低功耗模式的程序判断流程 标志位设置操作一定要在WFI/WFE之前&#xff0c;调用此指令后立即进入睡眠判断流程 模式对比 睡眠模式 停止模式 待机模式

springBoot是如何实现自动装配的

目录 1 什么是自动装配 2 Spring自动装配原理 2.1 SpringBootConfiguration ​编辑 2.2 EnableAutoConfiguration 2.2.1 AutoConfigurationPackage 2.2.2 Import({AutoConfigurationImportSelector.class}) 2.3 ComponentScan 1 什么是自动装配 自动装配就是将官方写好的的…

RuoYi 云服务器部署系统

一.为什么要部署 关于RuoYi-Vue是一个前后端分离的Web后台管理系统。部署在云服务器上让所有人都可以访问这是Web网站很正常的一个需求,只要我们将前端静态文件暴露在公网中,自然就部署好了。当然,要求是前端的静态资源可以访问到后端的接口,网站才会正常运行。 二.云服务器…

Spring系列篇 -- Bean的生命周期

目录 经典面试题目&#xff1a; 一&#xff0c;Bean的生命周期图 二&#xff0c;关于Bean的生命周期流程介绍&#xff1a; 三&#xff0c;Bean的单例与多例模式 总结&#xff1a; 前言&#xff1a;今天小编给大家带来的是关于Spring系列篇中的Bean的生命周期讲解。在了解B…

C语言:每日一练(选择+编程)

目录 选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;打印1到最大的n位数 示例1 思路一&#xff1a; 题二&#xff1a;计算日期到天数转换 示例1 思路一&#xf…

Leetcode-每日一题【剑指 Offer 32 - I. 从上到下打印二叉树】

题目 从上到下打印出二叉树的每个节点&#xff0c;同一层的节点按照从左到右的顺序打印。 例如: 给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 返回&#xff1a; [3,9,20,15,7] 提示&#xff1a; 节点总数 < 1000 解题思路 1.题目要求我们从…

每天一道leetcode:剑指 Offer 64. 求1+2+…+n(中等递归)

今日份题目&#xff1a; 求 12...n &#xff0c;要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句&#xff08;A?B:C&#xff09;。 示例1 输入: n 3 输出: 6 示例2 输入: n 9 输出: 45 提示 1 < n < 10000 题目思路 使用递归…

LVS-DR的RS进行ARP抑制的原因和LVS持久连接配置

一.RS的ARP抑制 1.为什么要抑制 2.如何抑制 &#xff08;1&#xff09;修改/etc/sysctl.conf文件&#xff0c;增加以下内容 &#xff08;2&#xff09;命令行临时设置 二.LVS持久连接 1.客户端持久连接 2.端口持久连接 3.防火墙标记持久连接 一.RS的ARP抑制 1.为什么要…

Redis系列(二):深入解读Redis的两种持久化方式

博客地址&#xff1a;blog.zysicyj.top Redis为什么要引入持久化机制 Redis引入持久化机制是为了解决内存数据库的数据安全性和可靠性问题。虽然内存数据库具有高速读写的优势&#xff0c;但由于数据存储在内存中&#xff0c;一旦服务器停止或崩溃&#xff0c;所有数据将会丢失…

matlab中exp和expm的区别

exp()为数组 X 中的每个元素返回指数 e x e^{x} ex expm()计算 X 的矩阵指数。 两个函数传入矩阵后计算的结果是不同的&#xff0c;千万不能混淆。之前曾经想当然得把exp里传入矩阵当矩阵指数使用&#xff0c;也未验证正确性&#xff0c;实不应该。

分割一切模型FastSAM,点哪里分割哪里

分割一切模型FastSAM点哪里分割哪里 VX搜索 《晓理紫》&#xff0c;关注并回复fastsampoint获取核心源码 [晓理紫] 1 效果 2 核心代码 在FastSAM ONNXRuntime部署&#xff0c;FastSAM TensorRT部署分别介绍了FastSAM通过OnnxRuntime以及TensorRT部署&#xff0c;通过点进行选取…