菜鸟笔记
提升您的技术认知

智能指针解决循环引用和线程安全问题-ag真人游戏

std::shared_ptr的线程安全问题

通过这个程序我们来测试shared_ptr的线程安全问题,需要注意shared_ptr的线程安全分为两方面:

// 1.演示引用计数线程安全问题,就把addrefcount和subrefcount中的锁去掉
// 2.演示可能不出现线程安全问题,因为线程安全问题是偶现性问题,main函数的n改大一些概率就变大了,就容易出现了。
// 3.下面代码我们使用sharedptr演示,是为了方便演示引用计数的线程安全问题,将代码中的sharedptr换成sshared_ptr进行测试,可以验证库的shared_ptr,发现结论是一样的。
void shareptrfunc(sharedptr& sp, size_t n) {
	cout << sp.get() << endl;
	for (size_t i = 0; i < n;   i)
	{
		// 这里智能指针拷贝会  计数,智能指针析构会--计数,这里是线程安全的。
		sharedptr copy(sp);
		// 这里智能指针访问管理的资源,不是线程安全的。所以我们看看这些值两个线程  了2n次,但是最
		终看到的结果,并一定是加了2n
			copy->_year  ;
		copy->_month  ;
		copy->_day  ;
	}
}
int main()
{
	sharedptr p(new date);
	cout << p.get() << endl;
	const size_t n = 100;
	thread t1(shareptrfunc, p, n);
	thread t2(shareptrfunc, p, n);
	t1.join();
	t2.join();
	cout << p->_year << endl;
	cout << p->_month << endl;
	cout << p->_day << endl;
	return 0;
}

1.智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时 或者--,这个操作不是原子的,引用计数原来是1, 了两次,可能还是2,这样引用计数就乱了,有可能造成资源未释放或者程序崩溃的风险。所以说智能指针中 或--的操作是需要加锁的,也就是说引用计数的操作是线程安全的

2.智能指针的对象存放在堆上,两个线程同时去访问,就会造成线程安全问题

 

std::shared_ptr循环引用

struct listnode
{
	int _data;
	shared_ptr _prev;
	shared_ptr _next;
	~listnode(){ cout << "~listnode()" << endl; }
};
int main()
{
	shared_ptr node1(new listnode);
	shared_ptr node2(new listnode);
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	node1->_next = node2;
	node2->_prev = node1;
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	return 0;
}
  1.  node1和node2两个智能指针对象指向两个节点,引用计数变为1,我们不需要手动delete
  2. node1的_next指向node2,node2的_prev指向node1,引用计数变成2
  3. node1和node2析构,引用计数减到1,但是_next还指向下一个节点,_prev指向上一个节点
  4. 也就是说_next析构了,node2释放了
  5. 也就是说_prev析构了,node1释放了
  6. 但是_next属于node的成员,node1释放了,_next才会析构,而node1由_prev管理,_prev属于node2成员,所以这就叫循环引用,谁都不会释放

ag真人游戏的解决方案

在引用计数的场景下,把shared_ptr换成weak_ptr就可以了

原理就是,node1->_next = node2; 和 node2->_prev = node1; 时weak_ptr的_next和_prev不会增加node1和node2的引用计数

struct listnode
{
int _data;
weak_ptr _prev;
weak_ptr _next;
~listnode(){ cout << "~listnode()" << endl; }
};
int main()
{
shared_ptr node1(new listnode);
shared_ptr node2(new listnode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
return 0; }

 

如果不是new出来的空间如何用智能指针管理呢?

其实shared_ptr设计了一个删除器来解决这个问题

// 仿函数的删除器
template
struct freefunc {
	void operator()(t* ptr)
	{
		cout << "free:" << ptr << endl;
		free(ptr);
	}
};
template
struct deletearrayfunc {
	void operator()(t* ptr)
	{
		cout << "delete[]" << ptr << endl;
		delete[] ptr;
	}
};
int main()
{
	freefunc freefunc;
	shared_ptr sp1((int*)malloc(4), freefunc);
	deletearrayfunc deletearrayfunc;
	shared_ptr sp2((int*)malloc(4), deletearrayfunc);
	return 0;
}