资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

多线程下对象的析构问题-创新互联

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

创新互联服务项目包括温州网站建设、温州网站制作、温州网页制作以及温州网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,温州网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到温州省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

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

懒汉模式:需要的时候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_lock(_Mutex);//加锁
		if (instance == nullptr)
		{
			instance = new Message();
		}
	}
	return instance;
}

void Message::Del_instance()//删掉对象
{
	lock_guard_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_lock(_Mutex);//加锁
		if (instance == nullptr)
		{
			instance = new Message();
			instance->AddWordThread();
		}
	}
	return instance;
}

void Message::Del_instance()//删掉对象
{
	lock_guard_lock(_Mutex);//加锁
	if (instance != nullptr)
	{
		delete instance;
		instance = nullptr;
	}
}
void Message::AddMess(const string& sm)//多线程..添加消息
{
	lock_guard_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_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_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//为了安全获得当前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_ptrinstance;

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

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

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

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

void Message::Del_instance()//删掉对象
{
	lock_guard_lock(_Mutex);//加锁
	if (instance != nullptr)
	{
		//delete instance;
		instance.reset();//当uses=0的时候 释放资源
		//nstance = nullptr;
	}
}
void Message::AddMess(const string& sm)//多线程..添加消息
{
	lock_guard_lock(this->_m_mutex);//加的是当前对象指向的锁
	//a线程输入 b就不要输入了
	message = sm;
}
void Message::AddWordThread()//创建线程
{
	//thread m_th(&Message::WordThread,this);//创建了一个线程
	//m_th.detach();//线程分离出去
	weak_ptrpa =shared_from_this();//获取this拥有资源的ptr
	pwth = new thread(&Message::WordThread, pa);
}
void Message::WordThread(weak_ptrpm)//工作线程
{
	for (;;)
	{
		shared_ptrpa = pm.lock();
		if (!pa)return;
		lock_guard_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_ptrpa = 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_ptrpa = 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;
}

完事收工

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


当前文章:多线程下对象的析构问题-创新互联
文章来源:http://cdkjz.cn/article/ceihgh.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

大客户专线   成都:13518219792   座机:028-86922220