通常,当强和弱引用计数都达到0时,智能指针(强或弱)将释放控制块。
在下面的场景中,我有一个问题要弄清楚这是如何实现的:线程A持有强引用,线程B持有弱引用,两者都指向同一个块。
假设强引用正在被销毁,并且强引用计数已达到0,引用析构函数将调用托管对象的析构函数。到目前为止,一切顺利。
但随后它会检查weakrefcount并根据该值决定解除分配块。
这里我仍然看到线程B中的弱引用和线程A中的强引用之间可能会有两次释放块的竞争。弱引用可能会将其弱引用计数原子地减少到0,但我不明白为什么强引用在弱引用释放之前不能看到这个0,并再次释放。
3条答案
按热度按时间qq24tv8q1#
如果我没理解错的话,你是在想象
这确实会导致
deallocate_block()
被调用两次,因此,这不是一个正确的实现。诀窍是使
shared_ptr
构造增量(和破坏减量)同时为强引用计数和弱引用计数:有一个
shared_ptr
和一个weak_ptr
未完成,弱计数将是2,而不是1。当两者都被销毁时,deallocate_block
将只被调用一次-由将弱计数递减到零的析构函数调用。作为一种优化,我们可以让给定控制块的 all outstanding
shared_ptr
s共享弱计数的一个增量的所有权,在这种情况下析构函数看起来像这样:在此设置中,如果有任何shared_ptr未完成,则弱计数将为
(# of outstanding weak_ptrs) + 1
。u4dcyp6a2#
shared_ptr
的控制块保证是线程安全的,并且在shared_ptr
的销毁和任何剩余的weak_ptr
示例之间不存在竞争。实际上,这可以通过控制块隐式地将自身视为弱引用来实现。换句话说,弱计数从1开始。当对象被销毁时,控制块也会释放一个弱引用。
例如,Microsoft的实现是这样的:
上面,当
_Uses
达到0时,对象被销毁,然后释放一个弱引用。如果这导致没有剩余的弱引用,则控制块被销毁。mzillmmw3#
弱计数器不是用来控制指针对象的分配/释放,而是用来控制指针控制块的分配/释放。
也就是说,使用shared_ptr时,通常会得到如下结构:
现在我们有2个
shared_ptr
s,所以ref计数为2。当它达到0时,我们销毁T
和控制块。然后再加上几个
weak_ptr
:这就产生了一点问题。我们想在ref计数达到0时删除
T
。但是当refs=0时我们不能删除控制块,因为这会给两个weak_ptr
留下它们“认为”指向一个控制块的指针,但是控制块现在已经消失了,所以它们有过时的指针。因此,我们添加第二个ref count:一个用于
shared_ptr
s的数量,一个用于share_ptr
s和weak_ptr
s的总数。第一个控制T
的删除,第二个控制控制块本身的删除。