论 shared_ptr的线程安全

shared_ptr 在 C++ 中并非设计为完全线程安全的,虽然原子操作保证了引用计数的线程安全,但原始指针和引用计数块的操作组合并不原子,可能导致数据竞争。多线程环境下,不同 shared_ptr 实例管理同一内存是线程安全的,而同一 shared_ptr 在多线程中管理同一内存则需要额外同步。因此,shared_ptr 只能确保在无数据竞争情况下析构的安全性,而它管理的内存本身并无线程安全保证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

杂谈

今天有同事问我shared_ptr是线程更安全的吗?我当时脑子一懵,有点不确定。
但回过神来仔细一想这什么鸟问题,c++ stl里有线程安全的吗,shared_ptr 也不是针对线程安全而设计出来的呀,八竿子打不着的东西为什么会凑在一起问。

好像也就一个atmoic引用计数可以沾上边。

shared_ptr 是个啥

首先,shared_ptr 往简单里说就是一个带引用计数的原始指针,引用计数自动控制原始指针资源释放;
另外,引用技术使用的是原子变量,引用增加减少都是原子操作;

从以上来看,他解决的是内存释放所有权的问题,充其量用原子的引用计数保证了正常使用下的多线程下的一块内存的释放的安全。什么是正常使用后面细说。

shared_ptr 本身的使用就不是线程安全的

虽然原子操作保证了引用计数的线程安全,但是shared ptr管理的是原始指针+引用计数块,原始指针和引用技术块这两个类成员操作加在一起并不是原子的,参照一篇12年的博客示例,两个成员变量的多线程数据同步使用必然是要加同步控制的。

当然 cppreference原文,如果对同一块管理同一块内存的不同shared ptr实例,多线程所有的成员函数都是是线程安全的,这个和上面的区别在于一个

  • 多线程管理同一个内存的同一个shared_ptr (不安全)
  • 多线程管理同一个内存的不同shared_ptr (安全)

All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same instance of shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions can be used to prevent the data race.

多线程管理同一个内存的不同shared_ptr ,对于原始指针来说,是有两个指针变量指向对一块内存,不同线程下的只针对这两个指针变量没有数据共享,自然就不存在数据竞争;至于对于内存块的引用计数则因为原子操作保证不需要额外加同步。说的有点绕,注意区分。

这也是为什么share_ptr使用值传递是标准姿势,多线程无脑使用值传递可以省很多事,而不是引用传递或者全局变量共享。

另外针对非要 多线程管理同一个内存的同一个shared_ptr 这种情况,c++11有提供一系列atmoic自由函数来保证操作shared_ptr原子化
c++20 则废弃了这一系列自由函数,引入了std::atomic <std::shared_ptr > 来原子操作同一个shared_ptr。

shared_ptr 裸指针指向的内存没有线程安全

还是我上面说的,shared_ptr 只是对一块内存做了引用计数,而这一块内存操作并没有提供任何形式的线程安全措施。
原始内存数据如果不是线程安全的,加上了一个shared_ptr形式的外部封装管理就支持线程安全了?不现实的。

结论

shared_ptr本身不是线程安全的,他只能保证不存在数据竞争的情况下,保证析构线程安全;
另外shared_ptr并没有给管理的数据提供任何形式的同步,管理的数据是线程安全的,通过shared_ptr也是安全的,管理的数据不是线程安全,那就不是线程安全的

在C++11中,std::shared_ptr是一种智能指针,它可以通过共享所有权的方式来管理对象的生命周期。多个std::shared_ptr可以指向同一个对象,当最后一个指向该对象的std::shared_ptr被销毁时,该对象也会被销毁。在多线程环境下,由于多个线程可能同时访问同一个std::shared_ptr对象,因此需要保证std::shared_ptr线程安全性。 Boost为shared_ptr提供了与内置类型同级别的线程安全性,这意味着: 1. 同一个shared_ptr对象可以被多线程同时读取。 2. 不同的shared_ptr对象可以被多线程同时修改。 这是通过使用引用计数和互斥锁来实现的。每个shared_ptr对象都有一个引用计数,用于记录有多少个shared_ptr对象指向同一个对象。当一个shared_ptr对象被复制时,它的引用计数会增加;当一个shared_ptr对象被销毁时,它的引用计数会减少。当引用计数为0时,对象会被销毁。 同时,为了保证线程安全性,Boost使用了互斥锁来保护shared_ptr对象的引用计数。当一个shared_ptr对象的引用计数被修改时,需要先获取互斥锁,以防止其他线程同时修改引用计数。这样可以确保在多线程环境下,shared_ptr对象的引用计数始终是正确的,从而保证了shared_ptr线程安全性。 示例代码如下: ```c++ #include <iostream> #include <memory> #include <thread> void thread_func(std::shared_ptr<int> p) { std::cout << "Thread 1: " << *p << std::endl; } int main() { std::shared_ptr<int> p(new int(42)); std::thread t(thread_func, p); std::cout << "Main thread: " << *p << std::endl; t.join(); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值