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的用法,这个是变法互斥基本要求
相关文章:
C++并发及互斥保护示例
最近要写一个多线程的并发数据库,主要是希望使用读写锁实现库的并发访问,同时考虑到其他平台(如Iar)没有C的读写锁,需要操作系统提供,就将读写锁封装起来。整个过程还是比较曲折的,碰到了不少问题,在此就简…...
新手常犯的错误,anzo capital昂首资本一招避免少走弯路
新手是不是经常交易中赚不到钱,今天anzo capital昂首资本就盘点一下新手常犯的错误,一招教你少走弯路。 一.随便选择交易账户 开立实时账户时选择正确的账户类型,anzo capital昂首资本教你比较所有提供的账户类型,选择最符合财务…...
Java Vue (el-date-picker组件) 前后端 关于时间格式数据的处理方法
前端使用 elment-ui 组件 el-date-picker 其中组件需要格式化时间,增加属性 value-format"yyyy-MM-dd" 后端 Java 接收参数类型 后端Dto 使用Date接收,并添加JsonFormat注解 JsonFormat(pattern"yyyy-MM-dd") private Date testTi…...
Python爬虫——scrapy_多条管道下载
定义管道类(在pipelines.py里定义) import urllib.requestclass DangDangDownloadPipelines:def process_item(self, item, spider):url http: item.get(src)filename ../books_img/ item.get(name) .jpgurllib.request.urlretrieve(url, filename…...
lombok启动不生效(什么方法都试了,可还是不生效怎么办 ?! 救救我)
使用IntelliJ IDEA 2021.1.3(Ultimate Edition)时提示Lombok不生效 java: You aren’t using a compiler supported by lombok, so lombok will not work and has been disabled. 方式一:我们手动更新一下版本到以下版本 <!--Lombok--&…...
element文本域禁止手动拉伸、两种方式、textarea
文章目录 style方式element自带的禁止拉伸方法建议 style方式 html <el-inputv-model"content":rows"3"class"r_n"type"textarea"maxlength"40"placeholder""style"height: 100%;" />css style…...
c#中lambda表达式缩写推演
Del<string> ml new Del<string>(Notify);//泛型委托的实例化,并关联Nofity方法 Del<string> ml new Del<string>(delegate (string str) { return str.Length; });//将Nofity变更为匿名函数 Del<string> ml delegate(string str)…...
无涯教程-PHP - 循环语句
PHP中的循环用于执行相同的代码块指定的次数。 PHP支持以下四种循环类型。 for - 在代码块中循环指定的次数。 while - 如果且只要指定条件为真,就会循环遍历代码块。 do ... while - 循环执行一次代码块…...
思维进化算法(MEA)优化BP神经网络
随着计算机科学的发展,人们借助适者生存这一进化规则,将计算机科学和生物进化结合起来,逐渐发展形成一类启发式随机搜索算法,这类算法被称为进化算法(Evolutionary Com-putation, EC)。最著名的进化算法有:遗传算法、进化策略、进化规划。与传统算法相比,进化算法的特点是群体搜…...
Kotlin 中的 设计模式
单例模式 饿汉模式 饿汉模式在类初始化的时候就创建了对象,所以不存在线程安全问题。 局限性: 1、如果构造方法中有耗时操作的话,会导致这个类的加载比较慢; 2、饿汉模式一开始就创建实例,但是并没有调用…...
Vulnhub: ICMP: 1靶机
kali:192.168.111.111 靶机:192.168.111.208 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.208 80端口的cms为Monitorr 1.7.6m 搜索发现该版本的cms存在远程代码执行 searchsploit monitorr 漏洞利用 nc本地监听&…...
我的创作纪念日(C++修仙练气期总结)
分享自己最喜欢的一首歌:空想フォレスト—伊東歌詞太郎 机缘 现在想想自己在CSDN创作的原因,一开始其实就是想着拿着博客当做自己的学习笔记,笔记嘛,随便写写,自己看得懂就ok了的态度凸(艹皿艹 )。也是用来作为自己学习…...
css的常见伪元素使用
1.first-line 元素首行设置特殊样式。 效果演示: <div class"top"><p>可以使用 "first-line" 伪元素向文本的首行设置特殊样式。<br> 换行内容 </p></div> .top p::first-line {color: red;} 2.first-lette…...
91. 解码方法
递归法:超时了 从字符串的后面向前计算,每一次递归都缩小子集 public class Solution {public int NumDecodings(string s) {return RecursiveAdd(s, s.Length - 1);}public int RecursiveAdd(string s, int index) {// 已经到最后一个元素if(index <…...
docker搭建opengrok环境2
引言: 虚拟机关闭后重新开启,理论上是需要重新启动一下docker的,以重新启动其中的服务。 命令基础: docker images:查看docker中现有的镜像 docker container ls -all:查看docker中目前在运行的containe…...
【校招VIP】java语言考点之ConcurrentHashMap1.7和1.8
考点介绍: ConcurrentHashMap是JAVA校招面试的热门考点,主要集中在1.7和1.8的底层结构和相关的性能提高。 理解这个考点要从map本身的并发问题出发,再到hashTable的低性能并发安全,引申到ConcurrentHashMap的分块处理。同时要理解…...
php如何实现5x+2x+1x=100
要实现5x 2x 1x 100的计算,可以使用PHP来解方程。以下是一个简单的PHP代码示例: php <?php $x 1; // 初始化x的值while (5*$x 2*$x 1*$x ! 100) { // 循环直到方程成立$x; // 每次循环增加x的值 }echo "x " . $x; // 输出x的值 ?…...
机器人项目:从 ROS2 切换到 ROS1 的原因
一、说明 机器人操作系统ROS是使用最广泛的机器人中间件平台。它在机器人社区中使用了10多年,无论是在业余爱好者领域还是在工业领域。ROS可用于各种微控制器和计算机,从Arduino到Raspberry Pi再到Linux工作站,它为电机控制器,视觉…...
Vault主题 - UiCore多用途Elementor WordPress主题
你可以使用Vault主题 – UiCore多用途Elementor WordPress主题构建什么? Vault主题拥有专业、像素级完美且干净的现代布局,几乎适合您需要的任何网站: 小型企业网站企业网站着陆页面权威博客销售和营销页面网上商店 自由职业者的最佳选择 …...
G0第26章:微服务概述与gRPCprotocol buffers
Go微服务与云原生 1、微服务架构介绍 单体架构(电商) SOA架构(电商) 微服务架构(电商) 优势 挑战 拆分 发展史 第一代:基于RPC的传统服务架构 第二代:Service Mesh(istio) 微服务架构分层 核心组件 Summar…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
