std::shared_ptr 内部结构,弱点数量超过预期
std::shared_ptr internals, weak count more than expected
在Advanced STL系列的一个episode (35:00)中,Stephan T Lavavej展示了_Weaks
,值为0的计数器决定何时删除_Ref_count
结构, 等于活着的数量 weak_ptr
,如果有活着就加 1 shared_ptrs
。他解释说这是必要的,因为线程安全:如果 _Weaks
只等于 weak_ptr
的数量,那么当最后一个 weak_ptr
超出范围时,也有必要检查 _Uses
, alive shared_ptr
s 的计数器,检查是否可以删除_Ref_count
。由于缺乏原子性,这是不可接受的。
假设_Uses
=存活数shared_ptr
s,_Weaks
=存活数weak_ptr
s,假设我们有以下场景:
(_Uses
= 0, _Weaks
= 1): 最后一个 weak_ptr
超出范围,递减_Weaks
(_Uses
= 0, _Weaks
= 0):如果_Uses
等于0,删除_Ref_count
结构
在多线程应用程序中,什么会出错,这迫使我们使用 _Weak
= 存活数 weak_ptr
+ (shared_ptr
数 ? 1 : 0) 实施?
想象一下下面的场景。线程A持有一个shared_ptr
,线程B持有一个对应的weak_ptr
。所以在你的实现中,我们有 _Uses == 1
和 _Weaks == 1
.
智能指针析构函数有两种可能的实现方式,但都存在问题。
实现方式一:自减,然后校验
B的weak_ptr
被摧毁。递减 _Weaks
。我们有 _Uses == 1
、_Weaks == 0
。 B准备检查_Uses
,但是...
上下文切换。
A的shared_ptr
被摧毁。递减 _Uses
。我们有 _Uses == 0
、_Weaks == 0
。开始销毁 _Ref_count
.
上下文切换。
B 现在开始检查 _Uses
。为 0。开始销毁 _Ref_count
.
两个线程现在都在销毁_Ref_count
。不好。
实现方式二:先检查,再自减
B的weak_ptr
被摧毁。检查 _Uses
。它是 1,不会发生破坏。 B 准备递减 _Weaks
,但是...
上下文切换。
A的shared_ptr
被摧毁。检查 _Weaks
。它是 1,不会发生破坏。递减 _Uses
。我们有 _Uses == 0
、_Weaks == 1
。完成。
上下文切换。
B 现在轮到递减 _Weaks
。我们有 _Uses == 0
、_Weaks == 0
。无事可做。
我们泄露了 _Ref_count
。不好。
在Advanced STL系列的一个episode (35:00)中,Stephan T Lavavej展示了_Weaks
,值为0的计数器决定何时删除_Ref_count
结构, 等于活着的数量 weak_ptr
,如果有活着就加 1 shared_ptrs
。他解释说这是必要的,因为线程安全:如果 _Weaks
只等于 weak_ptr
的数量,那么当最后一个 weak_ptr
超出范围时,也有必要检查 _Uses
, alive shared_ptr
s 的计数器,检查是否可以删除_Ref_count
。由于缺乏原子性,这是不可接受的。
假设_Uses
=存活数shared_ptr
s,_Weaks
=存活数weak_ptr
s,假设我们有以下场景:
(
_Uses
= 0,_Weaks
= 1): 最后一个weak_ptr
超出范围,递减_Weaks
(
_Uses
= 0,_Weaks
= 0):如果_Uses
等于0,删除_Ref_count
结构
在多线程应用程序中,什么会出错,这迫使我们使用 _Weak
= 存活数 weak_ptr
+ (shared_ptr
数 ? 1 : 0) 实施?
想象一下下面的场景。线程A持有一个shared_ptr
,线程B持有一个对应的weak_ptr
。所以在你的实现中,我们有 _Uses == 1
和 _Weaks == 1
.
智能指针析构函数有两种可能的实现方式,但都存在问题。
实现方式一:自减,然后校验
B的weak_ptr
被摧毁。递减 _Weaks
。我们有 _Uses == 1
、_Weaks == 0
。 B准备检查_Uses
,但是...
上下文切换。
A的shared_ptr
被摧毁。递减 _Uses
。我们有 _Uses == 0
、_Weaks == 0
。开始销毁 _Ref_count
.
上下文切换。
B 现在开始检查 _Uses
。为 0。开始销毁 _Ref_count
.
两个线程现在都在销毁_Ref_count
。不好。
实现方式二:先检查,再自减
B的weak_ptr
被摧毁。检查 _Uses
。它是 1,不会发生破坏。 B 准备递减 _Weaks
,但是...
上下文切换。
A的shared_ptr
被摧毁。检查 _Weaks
。它是 1,不会发生破坏。递减 _Uses
。我们有 _Uses == 0
、_Weaks == 1
。完成。
上下文切换。
B 现在轮到递减 _Weaks
。我们有 _Uses == 0
、_Weaks == 0
。无事可做。
我们泄露了 _Ref_count
。不好。