C++ shared_ptr是线程安全的吗?
导读:C++面试中有时会有这样一个问题,shared_ptr是线程安全的吗?对此问题,我们需要从三个并发场景进行考虑,拷贝shared_ptr的安全性、对shared_ptr赋值的安全性和读写shared_ptr指向内存区域的安全性。
对于以上问题,首先给出以下结论:
如果多个线程同时拷贝同一个shared_ptr对象,不会有问题,因为shared_ptr的引用计数是线程安全的。
如果多个线程同时修改同一个shared_ptr 对象,不是线程安全的。
如果多个线程同时读写shared_ptr指向的内存对象,不是线程安全的。
下面通过简单程序实验证明:
1. 引用计数更新,线程安全
这里我们讨论对shared_ptr进行拷贝的情况,由于此操作读写的是引用计数,而引用计数的更新是原子操作,因此这种情况是线程安全的。下面这个例子,两个线程同时对同一个shared_ptr进行拷贝,引用计数的值总是20001。
std::shared_ptr<int> p = std::make_shared<int>(0);
constexpr int N = 10000;
std::vector<std::shared_ptr<int>> sp_arr1(N);
std::vector<std::shared_ptr<int>> sp_arr2(N);
void increment_count(std::vector<std::shared_ptr<int>>& sp_arr) {
for (int i = 0; i < N; i++) {
sp_arr[i] = p;
}
}
std::thread t1(increment_count, std::ref(sp_arr1));
std::thread t2(increment_count, std::ref(sp_arr2));
t1.join();
t2.join();
std::cout<< p.use_count() << std::endl; // always 20001
2. 同时读写内存区域,线程不安全
下面这个例子,两个线程同时对同一个shared_ptr指向内存的值进行自增操作,最终的结果不是我们期望的20000。因此同时修改shared_ptr指向的内存区域不是线程安全的。
std::shared_ptr<int> p = std::make_shared<int>(0);
void modify_memory() {
for (int i = 0; i < 10000; i++) {
(*p)++;
}
}
std::thread t1(modify_memory);
std::thread t2(modify_memory);
t1.join();
t2.join();
std::cout << "Final value of p: " << *p << std::endl; // possible result: 16171, not 20000
3. 直接修改shared_ptr对象本身的指向,线程不安全。
下面这个程序示例,两个线程同时修改同一个shared_ptr对象的指向,程序发生了异常终止。
std::shared_ptr<int> sp = std::make_shared<int>(1);
auto modify_sp_self = [&sp]() {
for (int i = 0; i < 1000000; ++i) {
sp = std::make_shared<int>(i);
}
};
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(modify_sp_self);
}
for (auto& t : threads) {
t.join();
}
报错为:
pure virtual method called
terminate called without an active exception
用gdb查看函数调用栈,发现是在调用std::shared_ptr<int>::~shared_ptr()时出错,
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff7bc7859 in __GI_abort () at abort.c:79
#2 0x00007ffff7e73911 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007ffff7e7f38c in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007ffff7e7f3f7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007ffff7e80155 in __cxa_pure_virtual () from /lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x00005555555576c2 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() ()
#7 0x00005555555572fd in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() ()
#8 0x0000555555557136 in std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() ()
#9 0x000055555555781c in std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::operator=(std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>&&) ()
#10 0x00005555555573d0 in std::shared_ptr<int>::operator=(std::shared_ptr<int>&&) ()
#11 0x000055555555639f in main::{lambda()#1}::operator()() const ()
...
其原因为:在并发修改的情况下,对正在析构的对象再次调用析构函数,导致了未定义的行为,从而发生了此异常。
对程序加锁后,程序可正常运行:
std::shared_ptr<int> sp = std::make_shared<int>(1);
std::mutex m;
auto modify = [&sp]() {
// make the program thread safe
std::lock_guard<std::mutex> lock(m);
for (int i = 0; i < 1000000; ++i) {
sp = std::make_shared<int>(i);
}
};
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(modify);
}
for (auto& t : threads) {
t.join();
}
std::cout << *sp << std::endl; // running as expected, result: 999999
总结
- shared_ptr未对指向的对象内存区域有线程安全保护,因此并发读写对应内存区域是不安全的。
- 由于赋值操作涉及原内存释放、修改指针指向等多个修改操作,其过程不是原子操作,因此对shared_ptr进行并发赋值不是线程安全的。
- 对shared_ptr进行并发拷贝,对数据指针和控制块指针仅进行读取并复制,然后对引用计数进行递增,而引用计数增加是原子操作。因此是线程安全的。
你好,我是七昂,计算机科学爱好者,致力于分享C/C++、操作系统、技术架构等计算机基础知识。如果你有任何问题或者建议,欢迎随时与我交流。如果这篇内容对您有帮助,希望能得到您的点赞和关注,之后将会持续分享更多干货。感谢阅读和支持!希望我们能一起探索程序员修炼之道。
C++ shared_ptr是线程安全的吗?的更多相关文章
- shared_ptr的线程安全
1.9 再论shared_ptr 的线程安全 虽然我们借shared_ptr 来实现线程安全的对象释放,但是shared_ptr 本身不是100% 线程安全的.它的引用计数本身是安全且无锁的,但对象的 ...
- shared_ptr的线程安全性
一: All member functions (including copy constructor and copy assignment) can be called by multiple t ...
- shared_ptr智能指针源码剖析
(shared_ptr)的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化.根据文档 (http://www.boost.org/doc/ ...
- 为什么多线程读写 shared_ptr 要加锁?
https://www.cnblogs.com/Solstice/archive/2013/01/28/2879366.html 为什么多线程读写 shared_ptr 要加锁? 陈硕(giantch ...
- C++之shared_ptr总结
转自 http://blog.csdn.net/u013696062/article/details/39665247 Share_ptr也是一种智能指针.类比于auto_ptr学习.所以推荐先学习a ...
- shared_ptr 和auto_ptr智能指针
shared_ptr:计数的智能指针 它是一个包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针 ,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时 ...
- shared_ptr 用法
引入 shared_ptr 是c++为了提高安全性而添加的智能指针,方便了内存管理. 特点 shared_ptr 是通过指针保持对象共享所有权的智能指针.多个 shared_ptr 对象可占有同一对象 ...
- 深入理解智能指针之shared_ptr(一)
本文基于C++标准库源码分析shared_ptr,旨在搞清楚shared_ptr是什么,线程安全性等,目标能够安全的使用智能指针. (一)shared_ptr是一个类. 首先可以确定的是shared_ ...
- C++11智能指针
今晚跟同学谈了一下智能指针,突然想要看一下C++11的智能指针的实现,因此下了这篇博文. 以下代码出自于VS2012 <memory> template<class _Ty> ...
- Boost使用笔记(Smart_ptr)
我是Word写的,复制过来实在懒得在排版了,有兴趣的朋友可以去我的百度文库看,谢谢 http://wenku.baidu.com/view/34e485e2f61fb7360b4c653e.html ...
随机推荐
- SQLServer 的Distinct
distinct去除重复的数据(distinct是对整个结果集进行数据重复处理,不是针对某一列) -> 检查返回不重复的数据(对于整条记录不重复才会去除,如ID不一样) 用法:select di ...
- 将mysql的查询结果保存到文件中,并将文件从内网下载到外网服务器
方法一: 直接执行命令: mysql> SELECT ordersid from yws.callback_clockd ORDER BY ordersid ASC limit 0,10000 ...
- Mysql密码安全策略修改
Mysql5.7默认有密码安全策略,密码安全级别要求比较高,在测试环境中使用起来不方便,本经验将介绍如何修改Mysql的密码安全策略,解决ERROR 1819错误. 1:首先使用root用户连接mys ...
- Display、Visibility 和 Opacity 的区别
<style> .d1{ display: none; } .d2{ visibility: visible; } .d3{ opacity: 0; } </style> &l ...
- 几个适合Java开发者的免费IDEA插件
今天,给大家推荐几个好用且免费的IntelliJ IDEA插件.如果你还没有用过,可以尝试一下,也许对你的日常工作会有一定的效率提升噢! RestFulTool 如果你是一个RESTful服务的开发者 ...
- CF452C 题解
洛谷链接&CF 链接 题目简述 有 \(m \times n\) 张牌,有 \(n\) 个种类,每个种类有 \(m\) 张,现在抽一张放回,再抽一张,求这张牌与第一张抽出的牌种类相同的概率. ...
- 关于IE11点击的select框需要点击两次才能展开option选择框
需求:select是一个级联选择框,点击select框之后向后台请求,然后把请求的数据展示出来. 问题:绑定onclick或者onfocus的时候,需要点击select框两次,才能将option选择框 ...
- 常回家看看之largebin_attack
常回家看看之largebin_attack 先简单介绍一下什么是largebin largebin 是 glibc 的 malloc 实现中用于管理大块内存的一种数据结构.在 glibc 的内存分配中 ...
- Nuxt.js 环境变量配置与使用
title: Nuxt.js 环境变量配置与使用 date: 2024/7/25 updated: 2024/7/25 author: cmdragon excerpt: 摘要:"该文探讨了 ...
- 【教程】解决npm 报错 npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead.
问题描述 只要在控制台执行npm,不论有没有参数,都会有此警告: npm WARN config global `--global`, `--local` are deprecated. Use `- ...