1. 深入理解shared_ptr的线程安全性
在C++11引入的智能指针中,shared_ptr因其自动内存管理特性而广受欢迎。但关于它的线程安全性,很多开发者存在误解。让我们先明确一个基本事实:shared_ptr的引用计数机制是线程安全的,但它所管理的对象本身并不具备线程安全性。这个看似简单的结论背后,隐藏着许多值得深入探讨的技术细节。
1.1 引用计数为何线程安全
shared_ptr内部实现的核心是一个控制块(control block),这个控制块包含两个重要部分:引用计数器(ref_count)和弱引用计数器(weak_count)。当多个shared_ptr指向同一个对象时,它们共享同一个控制块:
cpp复制std::shared_ptr<int> p1(new int(10)); // 控制块创建,ref_count=1
std::shared_ptr<int> p2 = p1; // ref_count增加到2
标准库通过原子操作(std::atomic)来实现引用计数的增减,这意味着:
- 引用计数的修改是原子的,不会出现部分更新的情况
- 不同线程对同一个shared_ptr进行拷贝或销毁时,引用计数能保持正确
- 内存释放只会在最后一个shared_ptr销毁时发生一次
重要提示:虽然引用计数是线程安全的,但这仅保证控制块的操作安全,不涉及所管理对象本身的线程安全性。
1.2 对象访问的线程安全问题
考虑以下场景:
cpp复制std::shared_ptr<int> p(new int(0));
// 线程1
(*p)++;
// 线程2
(*p)++;
这种情况下,两个线程同时修改同一个int对象,会产生数据竞争(data race)。shared_ptr并没有为所管理的对象提供任何同步机制,因此必须由开发者自行处理:
cpp复制std::mutex mx;
// 线程函数内
{
std::lock_guard<std::mutex> lock(mx);
(*p)++; // 现在操作是线程安全的
}
