聊一聊share_ptr的线程安全性

本文解释了线程安全的概念,重点讨论了shared_ptr在多线程环境中的线程不安全问题,如引用计数的改变、指向的修改和数据内容的并发访问。特别指出,虽然按值传递的shared_ptr是线程安全的,但管理的STL容器的并发操作可能导致问题。

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

首先弄清楚线程安全的概念:

Thread safety is a computer programming concept applicable to multi-threaded code. Thread-safe code only manipulates shared data structures in a manner that ensures that all threads behave properly and fulfill their design specifications without unintended interaction. There are various strategies for making thread-safe data structures.

来翻译一下:线程安全是一种用于多线程代码的计算机编程概念。线程安全代码仅以确保所有线程正常运行并满足其设计规范的方式操作共享数据结构,这个过程中不会出现意外的情况。有多种策略可用于创建线程安全数据结构。

说白了,就是让共享数据的改变符合程序员的预期

那么share_ptr在什么情况会出现线程不安全问题呢?主要讨论以下几种:

引用计数器的改变

shared_ptr中除了有一个指针,指向所管理数据的地址。还有一个指针执行一个控制块的地址,里面存放了所管理数据的数量(常说的引用计数)、weak_ptr的数量、删除器、分配器等。

也就是说对于引用计数这一变量的存储,是在堆上的,多个shared_ptr的对象都指向同一个堆地址。在多线程环境下,管理同一个数据的shared_ptr在进行计数的增加或减少的时候是线程安全的吗?

答案是肯定的,这一操作是原子操作。

To satisfy thread safety requirements, the reference counters are typically incremented using an equivalent of std::atomic::fetch_add with std::memory_order_relaxed (decrementing requires stronger ordering to safely destroy the control block)

share_ptr改变指向

改变指向的情况分两种情况讨论:

改变的是同一个share_ptr对象

这时候确实是不是线程安全的。

当你在多线程回调中修改shared_ptr指向的时候,该shared_ptr内数据指针要修改指向,sp原先指向的引用计数的值要减去1,另外sp指向的引用计数值要加1。然而这几步操作加起来并不是一个原子操作,如果多少线程都在修改sp的指向的时候,那么有可能会出问题。比如在导致计数在操作减一的时候,其内部的指向,已经被其他线程修改过了。引用计数的异常会导致某个管理的对象被提前析构,后续在使用到该数据的时候触发core dump。

改变的是指向同一块内存的不同share_ptr对象 

这时候每个线程内看到的sp,他们所管理的是同一份数据,用的是同一个引用计数。但是各自是不同的对象,当发生多线程中修改sp指向的操作的时候,是不会出现非预期的异常行为的。 

指针指向的内容改变

尽管前面提到了如果是按值捕获(或传参)的shared_ptr对象,那么是该对象是线程安全的。(因为是传值,这里多了一个shre_ptr对象,引用计数器加1,即指向同一块内存的不同share_ptr对象)然而话虽如此,但却可能让人误入歧途。因为我们使用shared_ptr更多的是操作其中的数据,对齐管理的数据进行读写。尽管在按值捕获的时候shared_ptr是线程安全的,我们不需要对此施加额外的同步操作(比如加解锁),但是这并不意味着shared_ptr所管理的对象是线程安全的!

请注意这是两回事。

如果shared_ptr管理的数据是STL容器,那么多线程如果存在同时修改的情况,是极有可能触发core dump的。比如多个线程中对同一个vector进行push_back,或者对同一个map进行了insert。甚至是对STL容器中并发的做clear操作,都有可能出发core dump,当然这里的线程不安全性,其实是其所指向数据的类型的线程不安全导致的,并非是shared_ptr本身的线程安全性导致的。尽管如此,由于shared_ptr使用上的特殊性,所以我们有时也要将其纳入到shared_ptr相关的线程安全问题的讨论范围内。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值