合一episode (35:00) 高级 STL 系列,Stephan T Lavavej 展示了 _Weaks
,其值为 0 的计数器决定何时删除 _Ref_count
结构,等于存活的数量weak_ptr
,如果存在 shared_ptr
则加 1。他解释说这是必要的,因为线程安全:如果 _Weaks
仅等于 weak_ptr
的数量,那么当最后一个 weak_ptr
超出范围时它还需要检查 _Uses
,即事件 shared_ptr
的计数器,以检查是否可以删除 _Ref_count
。由于缺乏原子性,这是 Not Acceptable 。
假设 _Uses
= 活跃的 shared_ptr
数量,_Weaks
= 活跃的 weak_ptr
数量,想象一下我们有以下场景:
(
_Uses
= 0,_Weaks
= 1):最后一个weak_ptr
超出范围, 递减_Weaks
(
_Uses
= 0,_Weaks
= 0):如果_Uses
等于0 , 删除_Ref_count
结构
在多线程应用程序中,什么会出错,这迫使我们使用 _Weak
= number of alive weak_ptr
+ (number of 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
。不好。
关于c++ - std::shared_ptr 内部结构,弱计数超过预期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43297517/