C++ STL map迭代器失效问题

news2024/11/18 0:22:56

最近在开发过程中,定位一个问题的时候,发现多线程场景下大量创建和销毁某个C:\Windows\System32\reg.exe时出现了383个进程创建消息处理的接口,和384个进程销毁处理消息的接口都在等待锁,另外一个线程也在等锁,后面看了一下在处理进程创建和进程销毁的IPC消息处理所在类中有三把锁,执行流程都锁住了,猜测应该是某个线程持有锁没释放,导致其他并发线程锁住了,结合转储的dump和log日志,以及使用VS2017加载对应的dump,对并行堆栈中的线程进行分析,找了很久没发现问题。最后想了一下,是不是某个地方线程做了耗时或者同步阻塞操作导致的,或者线程中执行了死循环,排查后发现是因为一个同事在对map做循环遍历时,erase操作不当,导致某个地方迭代器失效,线程崩溃了,持有两把锁,其他所有线程都拿不到锁,导致IPC消息一直无法发送,最后程序无法升级。

为了上述模拟多线程访问死锁的问题,我简单写了个demo示例,在main函数中创建了两个线程,其中一个线程对std::map<std::string, int> g_cityMap数据做删除操作,另外一个线程对std::map<std::string, int> g_cityMap做数据打印操作。
代码如下:

#include <string>
#include <mutex>
#include <thread>
#include <map>
#include <chrono>
#include <iostream>

// 共享数据
std::map<std::string, int> g_cityMap = {
	{"Shanghai", 20000000},
	{"Wuhan", 13000000},
	{"Beijing", 17000000},
	{"Chongqing", 25000000}
};

// 共享数据锁
std::mutex g_cityMapMutex;

// 线程1的执行函数
// 对g_cityMap做删除操作
void thread_func1()
{
	std::unique_lock<std::mutex> lk(g_cityMapMutex);
	for (auto iter = g_cityMap.begin(); iter != g_cityMap.end(); iter++) {
		if (iter->first == "Chongqing") {
			g_cityMap.erase(iter);	// 此处g_cityMap对iter执行erase操作后,iter迭代器会失效
		}
	}
}

// 线程2的执行函数
// 对g_cityMap中的数据进行打印
void thread_func2()
{
	// 此处先休眠500ms,等待线程1先执行
	std::this_thread::sleep_for(std::chrono::milliseconds(500));
	std::unique_lock<std::mutex> lk(g_cityMapMutex);
	// 打印最终的g_cityMap
	for (auto iter : g_cityMap) {
		std::cout << "[" << iter.first << "," << iter.second << std::endl;
	}
}


int main()
{
	std::thread thr1(thread_func1);
	std::thread thr2(thread_func2);

	if (thr1.joinable()) {
		thr1.join();
	}

	if (thr2.joinable()) {
		thr2.join();
	}

	std::cin.get();

	return 0;
}

运行上面程序,程序会崩溃
多线程访问map场景下迭代器失效导致线程崩溃
迭代器失效
并行堆栈示意图
线程1在thread_func1函数的第26行执行g_cityMap.erase(iter);操作后,iter迭代器就失效了,导致跳转到for (auto iter = g_cityMap.begin(); iter != g_cityMap.end(); iter++) {这条语句中的iter++操作时,线程1所在线程会崩溃,如下图所示:
线程1的执行堆栈
再来看一下线程2(对应线程ID为7236)的执行堆栈,如下图所示:
线程2的执行堆栈
从上面可以看出,线程7236在代码第37行执行加锁处卡住了,因为g_cityMapMutex被线程19004持有未释放,此时线程7236会被卡住。

map迭代器失效问题

下面来看一下错误的map迭代器失效写法,代码如下:

#include <map>
#include <algorithm>
#include <iostream>
#include <mutex>

using std::map;

void mapTest()
{
  std::mutex appPackageInfoMutex;	// 应用map锁
  std::unique_lock<std::mutex> lk(appPackageInfoMutex);
  map<int, int> myMap;
  for (int i = 0; i < 10; i++)
  {
  	myMap.insert(std::make_pair(i, i + 1));
  }

  // 打印myMap
  std::cout << "Before erase: " << std::endl;
  for (auto iter : myMap) {
  	std::cout << "[key: " << iter.first << ", value: " << iter.second << " ]" << "\n";
  }
  std::cout << std::endl;

  for (auto iter = myMap.begin(); iter != myMap.end(); iter++)
  {
  	if (iter->first > 5) {

  		myMap.erase(iter);
  	}
  }

  // 打印剩余的myMap
  std::cout << "After erase: " << std::endl;
  for (auto iter : myMap) {
  	std::cout << "[key: " << iter.first << ", value: " << iter.second << " ]" << "\n";
  }
  std::cout << std::endl;
}

int main()
{
  mapTest();

  return 0;
}

程序运行结果如下:
map迭代器失效导致程序崩溃
上面程序的意图很明显:就是先往myMap中放置一些键值对的数据:
[0,1],[1,2],[2,3],[3,4],[4,5],[5,6][6,7],[7,8],[8,9],然后在遍历myMap时删除key值大于5的元素。再接着打印操作后的myMap。
迭代器失效导致程序崩溃
map迭代器失效
从上面的错误可以看出:程序报cannot increment value-initialized map/set iterator异常。

正确的map迭代器删除操作示例

正确的写法如下:

#include <map>
#include <algorithm>
#include <iostream>
#include <mutex>

using std::map;

/******************************************************************************
对于关联容器(如map, set,multimap,multiset),删除当前的iterator,
仅仅会使当前的iterator失效,只要在erase时,递增当前iterator即可。
这是因为map之类的容器,使用了红黑树来实现,插入、删除一个结点不会对其他结点造成影响。
erase迭代器只是被删元素的迭代器失效,但是返回值为void,
所以要采用erase(iter++)的方式删除迭代器。
*******************************************************************************/

void mapTest()
{
	std::mutex appPackageInfoMutex;	// 应用map锁
	std::unique_lock<std::mutex> lk(appPackageInfoMutex);
	map<int, int> myMap;
	for (int i = 0; i < 10; i++)
	{
		myMap.insert(std::make_pair(i, i + 1));
	}

	// 打印myMap
	std::cout << "Before erase: " << std::endl;
	for (auto iter : myMap) {
		std::cout << "[key: " << iter.first << ", value: " << iter.second << " ]" << "\n";
	}
	std::cout << std::endl;

	for (auto iter = myMap.begin(); iter != myMap.end(); )
	{
		if (iter->first > 5) {

			myMap.erase(iter++);
		} else {
			iter++;
		}
	}

	// 打印剩余的myMap
	std::cout << "After erase: " << std::endl;
	for (auto iter : myMap) {
		std::cout << "[key: " << iter.first << ", value: " << iter.second << " ]" << "\n";
	}
	std::cout << std::endl;
}

int main()
{
	mapTest();

	return 0;
}

运行结果如下图所示:
正确写法运行结果

参考文章

  • 【C++ STL】迭代器失效的几种情况总结
  • STL容器迭代器失效情况分析、总结
  • 迭代器失效的几种情况总结

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

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

相关文章

【数据结构实验】查找(二)基于线性探测法的散列表

文章目录 1. 引言2. 实验原理2.1 散列表2.2 线性探测法 3. 实验内容3.1 实验题目&#xff08;一&#xff09;输入要求&#xff08;二&#xff09;输出要求 3.2 算法实现三、实验设计3.3 代码整合 4. 实验结果 1. 引言 本实验将通过C语言实现基于线性探测法的散列表 2. 实验原理…

车载通信架构 —— 传统车内通信网络CAN(可靠性为王)

车载通信架构 —— 传统车内通信网络CAN(可靠性为王) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非…

cesium轨迹线(闪烁轨迹线)

cesium轨迹线(闪烁轨迹线) 下面有源码 实现思路 使用ellipse方法加载圆型,修改polyline中‘material’方法重写glsl来实现当前效果(cesium版本1.109) 示例代码 index.html <!DOCTYPE html> <html lang="en"><head

SQL Server秘籍:数据分隔解密,数据库处理新境界!

点击上方蓝字关注我 在数据数据过程中经常会遇到数据按照一定字符进行拆分&#xff0c;而在不同版本的SQL SERVER数据库中由于包含的函数不同&#xff0c;处理的方式也不一样。本文将列举2个版本的数据库中不同的处理方法。 1. 使用 XML 方法 在SQL SERVER 2016版本之前&#x…

VMware OpenSLP漏洞解决方案

PS&#xff1a;早期为客户做VMware检测的方法&#xff0c;大家如有遇到可参考 OpenSLP堆溢出漏洞攻击大量ESXI服务器&#xff0c;该漏洞编号为CVE-2021-21974&#xff0c;由 OpenSLP 服务中的堆溢出问题引起 大于以下版本则不受影响 ESXi versions 7.x prior to ESXi7…

LangChain 10思维链Chain of Thought一步一步的思考 think step by step

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

nodejs+vue+python+PHP+微信小程序-健身俱乐部在线管理平台的设计与实现-安卓-计算机毕业设计

随着经济的发展、财富的累积&#xff0c;人们生活水平、生活质量大幅度提高&#xff0c;生活环境得到明显改善&#xff0c;但是竞争激烈、人们生活压力大、生活节奏快加上饮食习惯和生活方式不合理导致国内 亚健康人群逐年增多。统计数据表明当前我国亚健康人群比例已经超过了7…

Linux-基本指令(1.0)

Linux是一个非常流行的操作的知识&#xff0c;并提供实例帮助读者更好地理解。让我们一起来学习吧&#xff01;系统&#xff0c;也是云计算、大数据、人工智能等领域的重要基础。学习Linux命令是Linux系统管理的基础&#xff0c;也是开发过程中必不可少的技能。本博客将介绍Lin…

思维模型 波纹效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。小变化&#xff0c;大影响。 1 波纹效应的应用 1.1 波纹效应在市场中的应用 2008 年&#xff0c;美国金融危机爆发&#xff0c;导致全球经济陷入衰退。这场危机的起因是美国房地产市场的崩…

【人工智能】Chatgpt的训练原理

前言 前不久&#xff0c;在学习C语言的我写了一段三子棋的代码&#xff0c;但是与我对抗的电脑是没有任何思考的&#xff0c;你看了这段代码就理解为什么了&#xff1a; void computerMove(char Board[ROW][COL], int row, int col) {while (1){unsigned int i rand() % ROW, …

基于 STM32 的温度测量与控制系统设计

本文介绍了如何基于 STM32 微控制器设计一款温度测量与控制系统。首先&#xff0c;我们将简要介绍 STM32 微控制器的特点和能力。接下来&#xff0c;我们将详细讨论温度传感器的选择与接口。然后&#xff0c;我们将介绍如何使用 STM32 提供的开发工具和相关库来进行温度测量和控…

nrm安装及使用

一、介绍 nrm 是一个 Node.js 的 registry 管理工具&#xff0c;它允许你快速地在不同的 npm registry 之间进行切换。通过使用 nrm&#xff0c;你可以方便地将 npm 的 registry 切换为淘宝镜像、npm 官方镜像或者其他定制的镜像&#xff0c;以加快包的下载速度。nrm仓库请点击…

MyBatisPlus入门介绍

目录 一、MyBatisPlus介绍 润物无声 效率至上 丰富功能 二、Spring集成MyBatisPlus 三、SpringBoot集成MyBatisPlus 一、MyBatisPlus介绍 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个MyBatis的增强工具&#xff0c;在MyBatis的基础上只做增强不做改变&#xff0c…

单调栈 模板

class Solution { public: //从后往前的方法 vector<int> dailyTemperatures(vector<int>& temperatures) {int n temperatures.size();vector<int> ans(n);//创建一个大小为n的数组stack<int> st;//这个时候栈中没有任何元素for(int i n-1;i &g…

存算一体还是存算分离?谈谈数据库基础设施的架构选择

从一则用户案例说起 某金融用户问&#xff0c;数据库用服务器本地盘性能好还是外置存储好&#xff1f;直觉上&#xff0c;本地盘路径短性能应该更好。然而测试结果却出乎意料&#xff1a;同等中等并发压力&#xff0c;混合随机读写模型&#xff0c;服务器本地SSD盘合计4万 IOPS…

Spring Boot配置文件 Spring日志文件相关的知识

在上文中&#xff0c;小编带领大家创建了一个Spring Boot项目&#xff0c;并且成功的执行了第一个SPring Boot项目&#xff08;在网页上运行hello world&#xff09; 那么&#xff0c;本文的主要作用便是带领大家走进&#xff1a;Spring Boot配置文件 && Spring日志文件…

图书管理系统源码,图书管理系统开发,图书借阅系统源码三框架设计原理和说明

TuShuManger项目简介和创建 这里一共设计了6个项目,主要是借助三层架构思想分别设计了主要的三层,包括model实体层,Dal数据库操作层,Bll业务调用层,其他有公共使用项目common层,DButitly提取出来的数据库访问层,下面我们分别创建每个项目和开始搭建整个过程 TuShuManger…

STK Components 基础篇

1.开发包 STK Components 访问AGI官网&#xff0c;注册并登录后&#xff0c;从官网下载开发包&#xff1a;https://support.agi.com/downloads/&#xff0c;下载成功后可以申请许可证&#xff0c;AGI会向你注册的邮箱地址发送有效期半年的使用授权许可文件&#xff08;lic文件…

ubuntu+Teslav100 环境配置

系统基本信息 nvidia-smi’ nvidia-smi 470.182.03 driver version:470.182.03 cuda version: 11.4 产看系统体系结构 uname -aUTC 2023 x86_64 x86_64 x86_64 GNU/Linux 下载miniconda https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/?CM&OA https://mi…

Co-DETR:DETRs与协同混合分配训练代码学习笔记

关于论文的学习笔记&#xff1a;Co-DETR:DETRs与协同混合分配训练论文学习笔记-CSDN博客 作者提出了一种新的协同混合任务训练方案&#xff0c;即Co-DETR&#xff0c;以从多种标签分配方式中学习更高效的基于detr的检测器。这种新的训练方案通过训练ATSS和Faster RCNN等一对多标…