shared_ptr 的线程安全性


 多线程环境下,调用不同的 shared_ptr 实例的成员函数是不需要额外的同步手段的,即使这些 shared_ptr 管理的是相同的对象。

 多线程对于同一个 shared_ptr 实例的读操作(访问)可以保证线程安全;但对于同一个 shared_ptr 实例的写操作(改变一个 shared_ptr 指向的对象)则需要同步,否则会发生 race condition。即 shared_ptr 的引用计数本身是线程安全且无锁的,但 shared_ptr 对象本身的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化

  • 多个线程可以同时读取一个 shared_ptr 对象;
  • 多个线程同时读写一个 shared_ptr 对象,则需要加锁。

 以上讨论是 shared_ptr 对象本身的线程安全级别,而非其管理的对象的安全级别,shared_ptr 所管理的对象的并发操作是否为线程安全的,取决于具体所管理的对象,必要时需要使用一些同步手段加以保证。


shared_ptr 的数据结构

 shared_ptr 是引用计数型智能指针,计数值保存在堆上动态分配的内存中。具体而言,shared_ptr<Foo>包含两个成员,一个是 Foo 类型的指针,指向被管理的对象;一个 ref_count 指针,指向堆上的控制块:

 由于 shared_ptr 间的拷贝涉及两个成员的复制,而这两步拷贝不会原子性地发生:

  • 步骤 1:复制 ptr 指针:
  • 步骤 2:复制 ref_count 指针,并递增引用计数(此递增为线程安全的):

多线程读 shared_ptr 是安全的

 一个全局的 shared_ptr:

shared_ptr<Foo> global_ptr;

 线程 1 到 N 运行:

void threadFunc(){
	shared_ptr<Foo> local = global_ptr;
}

 以上对于 shared_ptr 变量global的读取操作是线程安全的。


多线程无保护读写 shared_ptr 可能出现的 race condition

 而要写入一个 shared_ptr(即修改一个 shared_ptr 变量),由于涉及到对象的析构,则有可能带来 race condition。

 考虑以下场景,有三个 shared_ptr:

shared_ptr<Foo> g(new Foo);		// 线程间共享的 shared_ptr 对象
shared_ptr<Foo> x;		// 线程 A 的局部变量
shared_ptr<Foo> n(new Foo);		// 线程 B 的局部变量

 初始状态:

 线程 A 执行 x = g(即 read g),并完成了步骤 1,但还没来得及执行步骤 2。此时切换到了线程 B:
 同时线程执行 g = n(即 write g),并完成了两个步骤:
  • 先是步骤 1:
  • 再是步骤 2:
 此时修改 g 的 ref_count 指针,引用计数递减为 0,触发 Foo1 对象的销毁,x.ptr 成了空悬指针。

 最后回到线程 A,完成步骤 2:
 多线程无保护地读写 g,造成了 x 指针的空悬,这也是多线程读写同一个 shared_ptr 必须加锁的原因。

 当然 race condition 远不止这一种,其他线程交织还是可能造成其他错误。
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值