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;
}
- node1和node2两个智能指针对象指向两个节点,引用计数变为1,我们不需要手动delete
- node1的_next指向node2,node2的_prev指向node1,引用计数变成2
- node1和node2析构,引用计数减到1,但是_next还指向下一个节点,_prev指向上一个节点
- 也就是说_next析构了,node2释放了
- 也就是说_prev析构了,node1释放了
- 但是_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;
}