多线程下对象的析构问题

news2024/11/19 1:51:08

多线程遇上对象析构是个很麻烦的问题,这里我用一个多线程的单例模式去演示一下对象析构的问题

懒汉模式,加锁,线程安全

懒汉模式:需要的时候new一个对象,不需要的时候delete

(线程安全的懒汉)单例模式的基本代码示例:

class Message {
private:
	Message(){}
	Message(const Message&) = delete;
	Message& operator=(const Message&) = delete;
	~Message(){}

private:
	static Message* instance;//实例指针 
	//多线程下  多个线程,我们要给出互斥量
	static mutex _Mutex;
public:
	static Message* Get_instance();//获取实例对象
	static void Del_instance();//删除实例
};
Message* instance=nullptr;
mutex Message::_Mutex;

Message* Message::Get_instance()//获得对象
{
	if (instance == nullptr)  //两个if 多重判断 减少一下锁开销
	{
		lock_guard<mutex> _lock(_Mutex);//加锁
		if (instance == nullptr)
		{
			instance = new Message();
		}
	}
	return instance;
}

void Message::Del_instance()//删掉对象
{
	lock_guard<mutex> _lock(_Mutex);//加锁
	if (instance != nullptr)
	{
		delete instance;
		instance = nullptr;
	}
}

加入多线程的一些属性

代码如下:

class Message {
private:
	string message;//单例对象 a b c线程
	mutex _m_mutex;//有竞争 要互斥

	Message(){}
	Message(const Message&) = delete;
	Message& operator=(const Message&) = delete;
	~Message(){}

	void AddWordThread();//创建工作线程
private:
	static Message* instance;//实例指针 
	//多线程下  多个线程,我们要给出互斥量
	static mutex _Mutex;
	static void WordThread(Message* pm);//工作线程
public:
	static Message* Get_instance();//获取实例对象
	static void Del_instance();//删除实例

	void AddMess(const string& sm);//给此单例对象添加消息
};
Message* Message::instance=nullptr;
mutex Message::_Mutex;

Message* Message::Get_instance()//获得对象
{
	if (instance == nullptr)  //两个if 多重判断 减少一下锁开销
	{
		lock_guard<mutex> _lock(_Mutex);//加锁
		if (instance == nullptr)
		{
			instance = new Message();
			instance->AddWordThread();
		}
	}
	return instance;
}

void Message::Del_instance()//删掉对象
{
	lock_guard<mutex> _lock(_Mutex);//加锁
	if (instance != nullptr)
	{
		delete instance;
		instance = nullptr;
	}
}
void Message::AddMess(const string& sm)//多线程..添加消息
{
	lock_guard<mutex> _lock(this->_m_mutex);//加的是当前对象指向的锁
	//a线程输入 b就不要输入了
	message = sm;
}
void Message::AddWordThread()//创建线程
{
	thread m_th(&Message::WordThread,this);//创建了一个线程
	m_th.detach();//线程分离出去

}
void Message::WordThread(Message* pm)//工作线程
{
	for (;;)
	{
		lock_guard<mutex>_lock(pm->_m_mutex);//保护的是消息
		cout << "这是工作线程..." << endl;
		if (!pm->message.empty())
		{
			cout << pm->message << endl;
			pm->message.clear();
		}
	}
}
void fun()
{
	Message* pa = Message::Get_instance();//获得单例对象
	string sm[] = { "fun","fun_hello","fun_hhhh" };
	for (const auto& x : sm)
	{
		pa->AddMess(x);//添加消息
		//this_thread::sleep_for(chrono::milliseconds(100));
	}
	Message::Del_instance();//删除单例对象
}
int main()
{
	fun();
	return 0;
}

这里的AddMess和WorkThread我没有实现同步,只是说明一下当对象析构之后,线程还在运行会有什么问题?
1、对象可能先于线程死亡,WorkThread就会进不去,什么也不执行

每增添一个消息之后让他睡眠个100毫秒看看,工作线程也多睡会让他打印出来我们看看

void Message::WordThread(Message* pm)//工作线程
{
	for (;;)
	{
		lock_guard<mutex>_lock(pm->_m_mutex);//保护的是消息
		cout << "这是工作线程..." << endl;
		if (!pm->message.empty())
		{
			cout << pm->message << endl;
			pm->message.clear();
		}
		this_thread::sleep_for(chrono::milliseconds(300));
	}
}
void fun()
{
	Message* pa = Message::Get_instance();//获得单例对象
	string sm[] = { "fun","fun_hello","fun_hhhh" };
	for (const auto& x : sm)
	{
		pa->AddMess(x);//添加消息
		this_thread::sleep_for(chrono::milliseconds(100));
	}
	Message::Del_instance();//删除单例对象
}

程序崩溃--->在打印完毕之后,懒汉模式创建的单例对象已经被析构了,但是工作线程里的pm已经没有资源了工作线程就会奔溃

 ​​​​

如果要使他正常的话就得让工作线程结束之后,单例对象析构

解决方案:

让判断线程是否结束,如果结束了,线程join,给个bool类型stop用来判断所有线程走完没,走完了,修改析构函数让他为真,把线程对象至为空,当工作线程看到stop为真了,直接退出。

不过这么写针对这个用例来说能走,不过要是再加个funa线程函数,主线程一次走两个,走多个就会出现问题了。

完美解决,引入shared_ptr和weak_ptr:要知道对于c++智能指针在多线程上面发挥巨大作用

还得了解一下,enable_shared_from_this 的使用

有关std::enable_shared_from_this_Oorik

class Message:public enable_shared_from_this<Message>//为了安全获得当前this指针的智能指针
{
private:
	string message;//单例对象 a b c线程
	mutex _m_mutex;//有竞争 要互斥

	bool _stop;
	thread* pwth;//指向线程的指针
	Message():_stop(false),pwth(nullptr){}

	Message(const Message&) = delete;
	Message& operator=(const Message&) = delete;

	void AddWordThread();//创建工作线程	
public:

	~Message(){
		_stop = true;
		pwth->join();
		cout << "工作线程结束.." << endl;
		pwth = nullptr;
		cout << "Message结束.." << endl;
	}
private:
	//static Message* instance;//实例指针 
	static shared_ptr<Message>instance;

	//多线程下  多个线程,我们要给出互斥量
	static mutex _Mutex;
	static void WordThread(weak_ptr<Message>pm);//工作线程
public:
	//static Message* Get_instance();//获取实例对象
	static shared_ptr<Message>Get_instance();

	static void Del_instance();//删除实例

	void AddMess(const string& sm);//给此单例对象添加消息
	void SetStop() { _stop = true; }
};
//Message* Message::instance=nullptr;
shared_ptr<Message>Message::instance(nullptr);
mutex Message::_Mutex;

shared_ptr<Message> Message::Get_instance()//获得对象
{
	if (instance == nullptr)  //两个if 多重判断 减少一下锁开销
	{
		lock_guard<mutex> _lock(_Mutex);//加锁
		if (instance == nullptr)
		{
			//instance = new Message();
			instance = shared_ptr<Message>(new Message());
			instance->AddWordThread();
		}
	}
	return instance;
}

void Message::Del_instance()//删掉对象
{
	lock_guard<mutex> _lock(_Mutex);//加锁
	if (instance != nullptr)
	{
		//delete instance;
		instance.reset();//当uses=0的时候 释放资源
		//nstance = nullptr;
	}
}
void Message::AddMess(const string& sm)//多线程..添加消息
{
	lock_guard<mutex> _lock(this->_m_mutex);//加的是当前对象指向的锁
	//a线程输入 b就不要输入了
	message = sm;
}
void Message::AddWordThread()//创建线程
{
	//thread m_th(&Message::WordThread,this);//创建了一个线程
	//m_th.detach();//线程分离出去
	weak_ptr<Message>pa =shared_from_this();//获取this拥有资源的ptr
	pwth = new thread(&Message::WordThread, pa);
}
void Message::WordThread(weak_ptr<Message>pm)//工作线程
{
	for (;;)
	{
		shared_ptr<Message>pa = pm.lock();
		if (!pa)return;
		lock_guard<mutex>_lock(pa->_m_mutex);//保护的是消息
		//cout << "这是工作线程..." << endl;
		if (pa->_stop)
		{
			return;
		}
		if (!pa->message.empty())
		{
			cout << pa->message << endl;
			pa->message.clear();
		}
		//this_thread::sleep_for(chrono::milliseconds(300));
	}
}

void fun()
{
	shared_ptr<Message> pa = Message::Get_instance();//获得单例对象
	string sm[] = { "fun","fun_hello","fun_hhhh" };
	for (const auto& x : sm)
	{
		pa->AddMess(x);//添加消息
		this_thread::sleep_for(chrono::milliseconds(1));
		//让工作线程跑起来
	}
	Message::Del_instance();//删除单例对象
}
void funa()
{
	shared_ptr<Message> pa = Message::Get_instance();//获得单例对象
	string sm[] = { "funaa","funa_bb","funa_oo" };
	for (const auto& x : sm)
	{
		pa->AddMess(x);//添加消息
		this_thread::sleep_for(chrono::milliseconds(2000));
		//让工作线程跑起来
	}
	Message::Del_instance();//删除单例对象
}
int main()
{
	thread th(fun);
	
	thread tha(funa);
	th.join();
	tha.join();
	cout << "main over" << endl;
	return 0;
}

 完事收工

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

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

相关文章

服务注册配置中心Nacos

文章目录一. 前言二. 下载安装1. 下载安装包2. Windows环境安装3. Linux环境安装1. 单击模式启动2. 集群模式启动3. 远程web控制4. 注册为系统服务三. 基本使用1. 添加依赖2. 服务注册3. 配置实例集群属性4. 实例权重负载均衡5. 环境隔离6. 临时实例与非临时实例四. Nacos配置管…

Gradle学习笔记之文件操作

文章目录本地文件文件集合文件树文件拷贝归档文件Gradle中的文件操作方式有五种&#xff1a;本地文件、文件集合、文件树、文件拷贝和归档文件。 本地文件 比较简单&#xff0c;API跟java中的完全一致&#xff1a; task("test_file") {doFirst {def f1 file(&quo…

史上最强,这份在各大平台获百万推荐的Java核心手册实至名归

又逢“金九银十”&#xff0c;年轻的毕业生们满怀希望与忐忑&#xff0c;去寻找、竞争一个工作机会。已经在职的开发同学&#xff0c;也想通过社会招聘或者内推的时机争取到更好的待遇、更大的平台。 然而&#xff0c;面试人群众多&#xff0c;技术市场却相对冷淡&#xff0c;…

再学C语言13:字符串(4)——scanf函数

一、scanf函数的使用 scanf函数功能&#xff1a;把输入的字符串转换成各种形式&#xff08;整数、浮点数、字符、字符串&#xff09; scanf函数是printf函数的逆操作 scanf函数与printf函数一样使用控制字符串和参数列表 控制字符串指出输入将被转换成的格式 主要区别在参…

Qt实现表格控件

一、概述 最近在研究QTableView支持多级表头的事情&#xff0c;百度了下网上资料还是挺多的。实现的方式总的来说有2种&#xff0c;效果都还不错&#xff0c;最主要是搞懂其中的原理&#xff0c;做到以不变应万变。 实现多级表头的方式有以下两种方案 行表头和列表头都是用一…

网络空间安全——利用 CVE-2017-0213 提权

利用 CVE-2017-0213 提权 VE-2017-0213 是一个比较冷门的COM 类型混淆 (Type Confusion)漏洞。巧妙的利用该漏洞&#xff0c;可以实现本地的提权。该漏洞由著名的Google Project zero 发现。 下面就简单演示一下利用CVE-2017-0213漏洞简单提权&#xff0c; 首先下载CVE-2017…

【环境搭建】RocketMQ集群搭建

前置条件及效果图 条件&#xff1a; 两台服务器&#xff0c;个人是两台腾讯云服务器(其中嫖的朋友一个)&#xff1b; 版本&#xff1a; rocketmq-version:4.4.0rocketmq-console(mq控制台)Java&#xff1a;1.8maven:3.6.3 集群模式选择&#xff1a; 单master 这种方式风险…

【Django】第三课 基于Django超市订单管理系统开发

概念 本文在上一文之上&#xff0c;针对管理员&#xff0c;经理&#xff0c;普通员工身份的用户操作供应商管理模块功能。 功能实现 供应商管理模块属于业务功能&#xff0c;这里管理员不具备操作权限&#xff0c;而经理具备与供应商之间谈合作的实际需要&#xff0c;因此经…

Linux | 进程理解,fork | 进程地址空间

文章目录冯诺依曼体系结构的理解为什么要有内存的存在&#xff1f;操作系统的管理进程的理解系统调用接口进程的查看fork进程状态Linux进程具体的状态孤儿进程总结进程优先级怎样修改优先级&#xff1f;进程其他概念进程抢占进程地址空间利用代码验证地址区域验证堆区和栈区的增…

python3GUI--音乐播放器(精简版)By:PyQt5(附下载地址)

文章目录一&#xff0e;前言二&#xff0e;预览1.主界面2.歌单页3.歌词页4.播放列表5.mini6.设置三&#xff0e;心得1.解耦2.体验优化3.歌词显示四&#xff0e;总结一&#xff0e;前言 传送门&#xff1a; 1.python3GUI–打造一款音乐播放器By:PyQt5&#xff08;附下载地址&am…

LD_PRELOAD劫持

在前面UUCTF的uploadinject题&#xff0c;遇到了 LD_PRELOAD劫持&#xff0c;之前没遇见过&#xff0c;刚好借此机会学一学。不能小瞧这个变量&#xff0c;它甚至可以弹shell&#xff0c;绕过disable_functions&#xff0c;非常危险。下面来介绍一下这个变量&#xff0c;以及怎…

XDocReport使用入门

XDocReport 简介 XDocReport是GitHub上根据麻省理工学院许可证开源的Wrod导出框架。XDocReport可以根据ODT、Doc、Docx文档模板通过模板引擎语法&#xff08;Freemarker、Velocity&#xff09;转换为另外一种格式文档&#xff08;Doc、Docx、XHTML、PDF&#xff09;。 XDocR…

防沉迷管理系统

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 1、用户模块 1.1注册&#xff1a;用户通过注册生产账号&#xff0c;并在数据库存储数据 1.2登录&#xff1a;用户登录后…

k8s学习-CKA真题-k8s升级(kubeadm、kubelet、kubectl等)

目录题目解析命令准备工作升级组件升级kubectl、kubelet收尾结果killer 模拟环境题目解析解题参考题目 解析 结合博主当前环境&#xff0c;调整后题目为&#xff1a; 现有的 Kubernetes 集权正在运行的版本是 1.23.6&#xff0c;仅将主节点上的所有 kubernetes 控制面板和组件…

【语音处理】基于加权压力匹配方法(WPMM)的声音系统研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

【小程序】案例 - 本地生活(列表页面)

1. 演示页面效果以及主要功能 页面导航并传参 上拉触底时加载下一页数据 下拉刷新列表数据 2. 列表页面的 API 接口 以分页的形式&#xff0c;加载指定分类下商铺列表的数据&#xff1a; 接口地址 https://www.escook.cn/categories/:cate_id/shops URL 地址中的 :cate…

博泰应宜伦:智能汽车上攻时刻,需要“国家级”平台登场

作者 | 张祥威 编辑 | 王博汽车智能化转型的道路上&#xff0c;有个问题可能并非杞人忧天&#xff0c;而是值得整个行业警醒的。那就是&#xff1a; 中国的智能汽车发展&#xff0c;是否会被国外“卡脖子”&#xff1f; 卡脖子的担忧&#xff0c;其实也可以理解为&#xff0c;中…

【Linux】shell及其运行原理

目录1.什么是shell2.shell的功能3.shell的感性理解4.为什么不安装图形化界面1.什么是shell shell &#xff1a; 操作系统内核的外壳 通常来讲&#xff0c;计算机硬件是由运算器、控制器、存储器、输入/输出设备等硬件共同组成的&#xff0c;而让各种硬件设备各司其职且能协同运…

【基础强训】day3

一、选择题 &#x1f4a6;第1题&#xff1a; 以下程序的输出结果是&#xff08;&#xff09; #include <stdio.h> main() { char a[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, *p; int i; i 8; p a i; printf("%s\n", p - 3); } A 6 B 6789 C 6 D 789 B 先定义…

阳了之后,python实用工具之:疫情信息快速查看

嗨害大家好鸭&#xff01;我是小熊猫&#xff01;&#xff08;阳了个阳版&#xff09; &#xff08;先给大家消消毒&#xff09; 不好意思&#xff0c;很久没给大家更新了… 主要是小熊猫我不小心阳了… 大家要多注意自己的身体健康 多喝点热水 这个真的是个好东西 在家里稍…