你会不会处理多线程中的对象管理?

在这里插入图片描述

前言

本系列为《你会不会处理多线程中的XXXX》 。
本系列参考资料:陈硕的《Linux服务端多线程编程》、还有我的经验。
适用人群:要有一定的C++基础、要会百度、要有一定的Linux服务器编程经验。
本文语言比较粗糙,应该不至于引起什么不适,大家都是成年人了。

多线程与线程安全

看上面那张图,是不是能联想到多线程?

就那七个张伟,他们有一个共用属性,钱包里的钱。这天,张伟A在吃喝的时候,发现钱给没了,原因是张伟B拿去捐款了,那就很尴尬了。为了避免这种情况,怎么办?他们商量了一下,给钱包上个锁,是吧,谁要用谁上锁。上了锁谁都别用,用完再解锁,大家再用。
但是呢?今天张伟A在吃饭之前,看了下钱包,钱还够,但是总不能这会儿把钱包锁了吧,吃个饭那么久,别人都不要用了吗?所以他就没锁。就在这档口,张伟C买了个王者荣耀新出的皮肤,完了,我们可怜的张伟A要结账的时候,没钱了,又要刷盘子了。
所以说,这个锁啊,并不能百分百的就保证线程的安全。
像这种情况啊,那怎么办?那就在吃饭结账的时候看一眼有没有钱,没钱那就吃慢点,等着钱包的钱又有了再说。
这是操作系统的资源调度算法,拿来举个例子说线程安全。

本篇的主角,是对象与线程安全,
对象有什么线程安全的隐患?无非指针悬挂,内存泄漏;又或者多次释放,内存错乱。

参考博客:可重入函数对于线程安全的意义

对象的创建很简单

对象构造要做到线程安全,就一点要求:不要暴露自己,即不要泄露this指针。
那就是做到以下几点:

不要在构造函数中注册任何回调
不要在构造函数中将this传给跨线程对象
即时在构造函数最后一行也不行

对于第一点,如果非要回调函数才能构造,那就换二段式构造,先构造,在调用回调函数。
对于第三条,如果这个类是个基类呢?它构造完了并不是真的构造完了,还有子类等着呢。

之所以要这样设计(把this传给子类那另当别论),就是为了防止构造过程被打断,构造出一个半成品。

对象的销毁与竞态条件

对象析构,在多线程里,由于竞态的存在,变得扑朔迷离。
看个例子:

扫描二维码关注公众号,回复: 11314305 查看本文章
Foo::~Foo(){
	//拿锁
	//析构
	//解锁
}
void Foo::update(){
	//拿锁
	//数据操作
	//解锁
}

extern Foo *f;//共享资源
A进程操作
delete f;
f = NULL;

B进程操作
if(f)
{
	f->update();
}

那这就有一个很尴尬的情况了:
A在执行“析构”的时候,已经拿到了锁,而B通过了 f 的判断,因为那会儿指针还活着,然后被锁卡住了。
接下来会发生什么?不知道,因为对象析构的时候把锁也带走了。。。(锁属于对象,对象析构,锁也跑不了)

那怎么办?
别怕,参考博客:智能指针

一个动态创建的对象,是否还有效光看指针是看不出来的指针就是指向了一块内存而已,这块内存上的对象如果已经被销毁,那就根本不能访问。

shared_ptr/weak_ptr

shared_ptr是引用计数型智能指针,被纳入C11标准库。shared_ptr是一个类模板,它只有一个参数,使用起来很方便。

shared_str是强引用,只要有一个指向x对象的shared_ptr存在,该对象及不会被析构。
weak_ptr是弱引用,它不控制对象的生命周期,但是它知道对象是否还存在。如果对象存在,它可以升级成为shared_ptr。

讲这么多不如来个例子实在:

class Observer{
private:
	std::vector<weak_ptr<Observer>> vwo;	//像这样用啊
}

再聊聊C++内存安全

C++里面可能出现的内存问题大致有这么几个方面

  • 缓冲区溢出
  • 空悬指针/野指针
  • 重复释放
  • 内存泄漏
  • 不配对的new[]/delete
  • 内存碎片

对应解决:

  • std::vetor
  • shared_ptr/weak_ptr
  • scoped_ptr,只在对象析构的时候释放一次
  • scoped_ptr
  • std::vetor

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43762191/article/details/106624716