1.9 再论shared_ptr 的线程安全

虽然我们借shared_ptr 来实现线程安全的对象释放,但是shared_ptr 本身不是100% 线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为shared_ptr 有两个数据成员,读写操作不能原子化。根据文档11,shared_ptr 的线程安全级别和内建类型、标准库容器、std::string 一样,即:

一个shared_ptr 对象实体可被多个线程同时读取;

两个shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作;

如果要从多个线程读写同一个shared_ptr 对象,那么需要加锁。

请注意,以上是shared_ptr 对象本身的线程安全级别,不是它管理的对象的线程安全级别。

要在多个线程中同时访问同一个shared_ptr,正确的做法是用mutex 保护:

  1. MutexLock mutex; // No need for ReaderWriterLock
  2. shared_ptr<Foo> globalPtr;
  3. // 我们的任务是把globalPtr 安全地传给doit()
  4. void doit(const shared_ptr<Foo>& pFoo);

globalPtr 能被多个线程看到,那么它的读写需要加锁。注意我们不必用读写锁,而只用最简单的互斥锁,这是为了性能考虑。因为临界区非常小,用互斥锁也不会阻塞并发读。

为了拷贝globalPtr,需要在读取它的时候加锁,即:

  1. void read()
  2. {
  3. shared_ptr<Foo> localPtr;
  4. {
  5. MutexLockGuard lock(mutex);
  6. localPtr = globalPtr; // read globalPtr
  7. }
  8. // use localPtr since here,读写localPtr 也无须加锁
  9. doit(localPtr);
  10. }

写入的时候也要加锁:

  1. void write()
  2. {
  3. shared_ptr<Foo> newPtr(new Foo); // 注意,对象的创建在临界区之外
  4. {
  5. MutexLockGuard lock(mutex);
  6. globalPtr = newPtr; // write to globalPtr
  7. }
  8. // use newPtr since here,读写newPtr 无须加锁
  9. doit(newPtr);
  10. }

注意到上面的read() 和write() 在临界区之外都没有再访问globalPtr,而是用了一个指向同一Foo 对象的栈上shared_ptr local copy。下面会谈到,只要有这样的local copy 存在,shared_ptr 作为函数参数传递时不必复制,用reference to const 作为参数类型即可。另外注意到上面的new Foo 是在临界区之外执行的,这种写法通常比在临界区内写globalPtr.reset(new Foo) 要好,因为缩短了临界区长度。如果要销毁对象,我们固然可以在临界区内执行globalPtr.reset(),但是这样往往会让对象析构发生在临界区以内,增加了临界区的长度。一种改进办法是像上面一样定义一个localPtr,用它在临界区内与globalPtr 交换(swap()),这样能保证把对象的销毁推迟到临界区之外。练习:在write() 函数中,globalPtr = newPtr; 这一句有可能会在临界区内销毁原来globalPtr 指向的Foo 对象,设法将销毁行为移出临界区。

为什么多线程读写 shared_ptr 要加锁?

shared_ptr线程安全性分析

最新文章

  1. java 中的Scanner
  2. 西门子成立next47部门,斥资十亿欧元投资VR/AR等初创公司
  3. ASP.NET Identity 3.0教程
  4. Angular JS 学习之 Scope作用域
  5. Android项目结构分析
  6. word2007
  7. 手把手教你入门mac idea
  8. 学习C++ Primer 的个人理解(三)
  9. CQRS学习——IOC,配置,仓储隔离以及QueryEntry[其三]
  10. eclipse中使用loadrunner java api步骤
  11. linux dd使用记录
  12. phpcmsV9.5.8 后台拿shell
  13. C语言的抽象与函数指针--思想(转)
  14. Python with/as和contextlib上下文管理使用说明
  15. Mac 10.9.3 自带PHP5.4.24增加redis和xdebug扩展
  16. jetty安装、配置、优化
  17. 计算概论(A)/基础编程练习1(8题)/7:奇数求和
  18. STL 源代码剖析 算法 stl_algo.h -- search_n
  19. ARC075 E.Meaningful Mean(树状数组)
  20. Flask&amp;&amp;人工智能AI --5 Flask-session、WTForms、数据库连接池、Websocket

热门文章

  1. mac 安装软件提示权限不足的解决的方法
  2. MySQL单列索引和组合索引的选择效率与explain分析
  3. UESTC 1511(差分约束)
  4. 编程之美 set 6 寻找最近点对
  5. 使用openstack部署云计算服务环境
  6. @Override错误
  7. System.getProperty()方法大全 (转载)
  8. k8s滚动升级
  9. Quartz.net 基于配置的调度程序实践
  10. hdu1568&amp;&amp;hdu3117 求斐波那契数前四位和后四位