【C++】shared_ptr的线程安全及循环引用问题

std::shared_ptr的线程安全问题

shared_ptr的线程安全分为两方面:

  1. 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或--,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2,这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以智能指针中引用计数++、--是需要加锁的,也就是说引用计数的操作是线程安全的。
  2. 智能指针管理的对象存放在堆上,两个线程同时去访问,会导致线程安全问题。

在我们前面所写的shared_ptr中,我们在进行AddRefCount操作时,进行了加锁和解锁,如果想要看看引用计数线程安全问题,去掉这里的锁即可。

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;
}

【C++】shared_ptr的线程安全及循环引用问题_第1张图片

分析如下:

  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属于node1的成员,node1释放了,_next才会析构,而node1由_prev管理,_prev属于node2的成员,所以这就叫循环引用,谁都不会释放。

解决方案:

在引用计数的场景下,把节点中的_prev和_next改成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;
}

 

你可能感兴趣的:(学习与总结)