std::shared_ptr构造与析构是否线程安全?多线程竞争场景疑问
问题1:std::shared_ptr的构造函数与析构函数是否具备线程安全性?
std::shared_ptr的拷贝/移动构造函数、析构函数本身不具备线程安全性——如果多个线程同时对同一个shared_ptr实例执行读写操作(比如一个线程析构它,另一个线程拷贝它),会引发数据竞争,行为属于未定义。- 但注意:多个线程操作不同的
shared_ptr实例(但指向同一个托管对象)是安全的,因为它们操作的共享引用计数,标准要求必须是原子操作。
问题2:假设线程1中存在一个std::shared_ptr sptr;线程1返回时触发sptr的析构函数,与此同时线程2调用构造函数对sptr进行拷贝;仅通过单个原子操作的std::shared_ptr如何保证该竞争场景下代码不会崩溃?
这种竞争场景的安全保障核心在于引用计数的原子性操作和析构的条件判断逻辑:
- 线程2拷贝
sptr时,会原子地将引用计数加1——这个操作是线程安全的,即便线程1同时在执行析构的引用计数减1,原子操作能保证计数修改的完整性和正确性,不会出现中间错误状态。 - 线程1的析构流程是:先原子地将引用计数减1,随后检查计数是否为0。如果减1后计数仍大于0,说明还有其他
shared_ptr持有该对象,直接结束析构流程,不会销毁托管对象;只有当计数变为0时,才会真正执行托管对象的析构。 - 原子操作的特性确保了,不管线程1的减1和线程2的加1操作谁先谁后,最终的引用计数结果都是正确的:要么线程2的加1先完成,线程1减1后计数不为0,对象不会被销毁;要么线程1的减1先执行但计数未到0(因为线程2的加1操作已经在原子操作队列中完成),同样不会错误销毁对象。
- 本质上,原子引用计数杜绝了“对象被销毁后仍有
shared_ptr指向它”的情况,从根源上避免了崩溃。
内容的提问来源于stack exchange,提问作者shengguo lu
相关产品推荐
相关产品推荐

