论 shared_ptr的线程安全

杂谈

今天有同事问我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 来原子操作同一个shared_ptr。

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

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

结论

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

你可能感兴趣的:(杂谈,C\C++,java,开发语言)