C++多线程场景中的变量提前释放导致栈内存异常

多线程场景中的栈内存异常

在子线程中尝试使用当前函数的资源,是非常危险的,但是C++支持这么做。因此C++这么做可能会造成栈内存异常。

正常代码

#include <iostream>
#include <thread>
#include <windows.h>// 线程函数,用于执行具体的任务
void fun(int param)
{std::cout << __FUNCTION__ << " param:" << param << std::endl;Sleep(3000);param = 1;std::cout << __FUNCTION__ << " param:" << param << std::endl;
}// 线程函数,用于执行具体的任务
void threadFunction() {// 在这里执行线程的具体任务int num = 2;//在函数中还启动了一个线程std::thread funThread([num](){fun(num);});funThread.detach();
}int main() {const int numThreads = 3;std::thread threads[numThreads];// 创建并启动多个线程for (int i = 0; i < numThreads; ++i) {threads[i] = std::thread(threadFunction);}//等待所有线程执行完毕for (int i = 0; i < numThreads; ++i) {threads[i].join();}Sleep(1000);std::cout << "All threads have completed." << std::endl;return 0;
}

上述是一个正常的多线程代码。

异常代码

但是如果将其中多线程传参设置为引用传递,可能就会造成栈内存异常了,如下所示:

#include <iostream>
#include <thread>
#include <windows.h>// 线程函数,用于执行具体的任务
void fun(int& param)//传参修改为引用传递
{std::cout << __FUNCTION__ << " param:" << param << std::endl;Sleep(3000);param = 1;std::cout << __FUNCTION__ << " param:" << param << std::endl;
}// 线程函数,用于执行具体的任务
void threadFunction() {// 在这里执行线程的具体任务int num = 2;//在函数中还启动了一个线程,传参为引用std::thread funThread([&num](){fun(num);});funThread.detach();
}int main() {const int numThreads = 3;std::thread threads[numThreads];// 创建并启动多个线程for (int i = 0; i < numThreads; ++i) {threads[i] = std::thread(threadFunction);}//等待所有线程执行完毕for (int i = 0; i < numThreads; ++i) {threads[i].join();}Sleep(1000);std::cout << "All threads have completed." << std::endl;return 0;
}

编译成功,但是运行失败。
运行结果:
在这里插入图片描述
上面std::thread funThread(&num{fun(num);});,采用引用传递,并且void fun(int& param)中也采用引用入参的形式。此时可能就会产生内存异常。

因为传入参数num是一个局部参数,我们在fun中修改或读取param时,可能num已经被释放掉了,这时候修改或者读取param就会发生内存错误,其实这里是栈内存异常。这是非常危险的,因为栈内存可能会造成无法估量的问题。本代码中只有这一个作业线程,所以就在这里报错了,所以就在任务函数中崩掉了。但是因为代码中可能还可以读取修改参数param,而导致其他线程中的异常,这种问题往往很难定位。

优化代码

当然我们用引用传递的好处是可以避免临时变量的产生,但变量是复杂对象时,还是可以很大程度减少内存消耗。虽然不能用引用,但是我们可以使用std::move来实现变量所有权的转移,也可以减少临时变量的拷贝。

#include <iostream>
#include <thread>
#include <windows.h>// 线程函数,用于执行具体的任务
void fun(int&& param)//右值传参
{std::cout << __FUNCTION__ << " param:" << param << std::endl;Sleep(3000);param = 1;std::cout << __FUNCTION__ << " param:" << param << std::endl;
}// 线程函数,用于执行具体的任务
void threadFunction() {// 在这里执行线程的具体任务int num = 2;//在函数中还启动了一个线程std::thread funThread(fun, std::move(num));//使用move传入右值funThread.detach();
}int main() {const int numThreads = 3;std::thread threads[numThreads];// 创建并启动多个线程for (int i = 0; i < numThreads; ++i) {threads[i] = std::thread(threadFunction);}//等待所有线程执行完毕for (int i = 0; i < numThreads; ++i) {threads[i].join();}Sleep(1000);std::cout << "All threads have completed." << std::endl;return 0;
}

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

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

相关文章

Ubuntu安装最新版neovim

Ubuntu安装最新版neovim 一、前言 对于neovim版本很重要&#xff0c;有很多插件几乎都要要求neovim版本在0.8或者0.9。但是有一个很严重的问题就是&#xff0c;Ubuntu使用sudo apt install neovim的版本很低达不到要求&#xff08;写文章时是0.7&#xff09; 二、解决方法 …

开发测试框架一 - 创建springboot工程及基础操作

一、创建及运行方式 1. 从官网导入&#xff1a; 注意&#xff1a;由于我的java版本是1.8&#xff1b;所以选中了spring2.7.14&#xff1b;如果你的java版本是9及以上&#xff0c;选中spring3相关的同时Java 版本也要对应起来 2. 创建第一个get请求 创建Controller package及…

用easyui DataGrid编辑树形资料

easyui显示编辑树形资料有TreeGrid元件&#xff0c;但是这个元件的vue版本和react版本没有分页功能。virtual scroll功能也表现不佳。 我用DataGrid来处理。要解决的问题点&#xff1a; &#xff08;1&#xff09;如何显示成树形。即&#xff0c;子节点如何有缩进。 先计算好…

Spring Clould 消息队列 - RabbitMQ

视频地址&#xff1a;微服务&#xff08;SpringCloudRabbitMQDockerRedis搜索分布式&#xff09; 初识MQ-同步通讯的优缺点&#xff08;P61&#xff0c;P62&#xff09; 同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&…

程序员与ChatGPT的交织:探索人工智能和软件开发的新篇章

目录 前言创作者程序员会被替代吗程序员如何更好的使用chatgpt 前言 在技术持续进步的当今世界&#xff0c;程序员与人工智能&#xff08;AI&#xff09;之间的关系越来越紧密。特别是对于一些创新性的技术如OpenAI旗下的ChatGPT&#xff0c;这种联系就更为明显。程序员与Chat…

vue 使用indexDB 简单完整逻辑

1 npm npm install idb 2 代码 <template><div><p>Data: {{ data }}</p><button click"fetchData">Fetch Data</button></div> </template><script> import { openDB } from idb;export default {data() {…

KU Leuven TU Berlin 推出“RobBERT”,一款荷兰索塔 BERT

荷兰语是大约24万人的第一语言&#xff0c;也是近5万人的第二语言&#xff0c;是继英语和德语之后第三大日耳曼语言。来自比利时鲁汶大学和柏林工业大学的一组研究人员最近推出了基于荷兰RoBERTa的语言模型RobBERT。 谷歌的BERT&#xff08;来自Transformers的B idirectional …

java请求SAP系统,发起soap的xml报文,实体类转换,idea自动生成教程

1、将接口的网页地址&#xff0c;右键保存&#xff0c;然后修改文件后缀为wsdl文件 2、idea全局搜索 wsdl&#xff0c;找到自动转换javabean插件&#xff1a; 3、点击后&#xff0c;选择下载改完后缀的文件&#xff1a; 4、将无用的class文件删除掉 5、请求sap的地址为&#…

MySQL语法及常用数据类型

一、SQL语言概述 对数据库进行查询和修改操作的语言叫做SQL。SQL的含义就是结构化查询语言&#xff08;Structured Query Language&#xff09;。SQL包含以下4个部分&#xff1a; 1、数据定义语言&#xff08;DDL&#xff09;&#xff1a;DROP、CREATE、ALTER等语句&#xff…

Mysql复制类型、主从复制集群种类、主从复制原理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 主从复制原理主从复制原理图一、概述二、为什么要读写分离&#xff1f;三、mysql支持的复制类型1、基于语句的复制statement&#xff1a;2、基于行的复制row&#x…

智能合约 -- 常规漏洞分析 + 实例

1.重入攻击 漏洞分析 攻击者利用合约漏洞&#xff0c;通过fallback()或者receive()函数进行函数递归进行持续取钱。 刚才试了一下可以递归10次&#xff0c;貌似就结束了(version: 0.8.20)。 直接看代码: 银行合约&#xff1a;有存钱、取钱、查看账户余额等函数。攻击合约:…

收集的一些比较好的git网址

1、民间故事 https://github.com/folkstory/lingqiu/blob/master/%E4%BC%A0%E8%AF%B4%E9%83%A8%E5%88%86/%E4%BA%BA%E7%89%A9%E4%BC%A0%E8%AF%B4/%E2%80%9C%E6%B5%B7%E5%BA%95%E6%8D%9E%E6%9C%88%E2%80%9D%E7%9A%84%E6%AD%A6%E4%B8%BE.md 2、童话故事 https://gutenberg.org/c…