为什么std :: shared_ptr没有使用引用链接?

时间:2013-01-12 08:43:31

标签: c++ c++11 shared-ptr smart-pointers reference-counting

std::shared_ptr需要在堆上分配一个包含引用计数的控制块。我从http://ootips.org/yonat/4dev/smart-pointers.html学到了另一种方法,它将所有引用保存在双向链表中。它不需要额外的分配或计数器,但参考对象本身更大。

是否有基准或任何明确的理由表明一种实施方式优于其他实施方案?

1 个答案:

答案 0 :(得分:8)

标准在理论上允许使用链表,但因为复制shared_ptr必须是线程安全的,所以用链表实现它会更难。该列表需要由互斥锁保护(或者是一个无锁列表,这更加困难),这样每次复制shared_ptr或超出范围时,列表都可以被多个线程安全地修改。 / p>

使用原子类型进行引用计数并使用原子操作进行引用计数更新通常更加简单并且通常更有效。

编辑:要回答下面的评论,仅使用原子指针来实现链接列表是不够的。要在列表中添加或删除节点(对应于增加和减少use_count),您需要以原子方式更新两个指针,以便在添加/删除节点之前和之后调整节点中的链接。 std::atomic<T*>允许您以原子方式更新单个指针,但如果您需要以原子方式更新两个此类对象,则无效。由于这两个指针位于不同的节点中,因此它们不相邻,因此即使是四字形CAS也无济于事。

替代方案是保护整个列表的互斥体(显然不利于争用)或每个列表节点的互斥锁,其中您锁定任何更新中涉及的每个节点的互斥锁,其使用更多内存并一次最多影响三个节点,即需要锁定三个互斥锁。如果use_count()小于或等于5,那么复制/销毁任何一个shared_ptr都会复制/销毁共享同一指针所有权的任何其他实例。对于高使用率计数,您可能会获得更少的争用,其中大多数更新是针对彼此远离的非邻居节点,但可能不是在一般情况下。大量程序使用shared_ptr使用单位数的使用计数。即使使用计数很高并且在任何节点上都没有争用,您仍然必须锁定三个互斥锁并创建/销毁列表节点(可能需要堆分配)并更新其相邻节点中的指针,因此原子递增/递减更简单,尽管存在原子整数争用,但仍然可以更快。

上次我在委员会反馈中提到,shared_ptr不需要使用引用计数,并且可以使用列表我得到了回复:

  

有没有人真的这样做,因为标准现在认可多线程?

和(参考线程安全要求):

  

为参考链接实现(高效)工作要困难得多。我甚至不确定它是否可能,尽管它可能是。