简单地说,没有。
一种竞争条件,即最终结果取决于特定的操作顺序,只有在引用计数上才有可能,一个操作可以使一个对计数有贡献的对象从一个没有,即当你有弱引用时。
这里算作竞速端点的是资源是否因为 refcount (RC) 达到零而被释放; “哪个线程执行的哪个确切操作使 RC 为零”这个问题是一个有趣的端点:使用 RC 在多线程上下文中管理资源的隐含假设是任何线程(具有最后一个所有者)都可以释放资源。
根据定义,RC 是每个所有者对 RC 的严格积极贡献的总和(恰好为 1,因为 RC 是所有者的数量,但这不是很重要)。在抽象设置中,RC 也可以形式化为所有者集合,并且由于 RC 的特性,整数将是所需信息的有效表示:
- 每个所有者都知道它在集合中
- 业主互不认识
- 每个所有者需要在添加到集合时将数字增加一定数量(根据定义为 1,但可以是任何严格的正数),并在从集合中移除时减少相同的数量李>
因此,您可以将数字本质上想象为所有者列表,每个所有者都由一条垂直线表示,就像孩子们学习数字时一样 (3 = |||),并且只有单个所有者知道自己的栏(您可以说所有条形都相同或颜色不同)。 (整数显然在物理上以二进制表示。)
在只有所有者存在的设置中(没有操作可以通过“弱引用”来引用 RC),对于所有者集只有两个基本操作:
复制只是添加了一个竖线。在图形显示中,您甚至可以通过擦除中间的条来拆分条,以两个半条结束。这是所有权被解散(就像您出售了交易公司的部分股份一样)。
删除操作会擦除属于所有者的竖线(当然在实践中没有标识的竖线,根本没有竖线,它是对二进制表示的整数的减量操作); 如果该操作删除了那个最后一根,那么删除线程负责释放资源。
您可以很容易地看到,零 RC 对应于一组空的柱,这发生在所有所有者都放弃拥有之后。有一个竞争条件可以确定在任何给定时间的确切柱数,但这是一个细节:每个线程中的每个所有者都知道他是所有者,这很重要。它本质上与 其他 所有者的数量无关。 (如果您不是无动于衷,那么您可能首先希望拥有唯一的所有权,以便能够在不影响其他用户的情况下更改资源。)
在集合上存在竞争条件意味着必须使用内部原子(或类似的替代方案),但所有者一般不应该关心。如果在表示中意外删除了一个特定的“条”,这将是灾难性的,这意味着没有考虑所有者并且它将是僵尸所有者:它会相信它拥有资源,但实际上它不会拥有任何东西.原子 RMW(读修改写)操作保证不会发生:任何被 RMW 操作独占修改的原子对象不能丢失修改。
所有权的概念暗示它不能从无到有:你只能成为某物的所有者:
- 通过创造那个东西
- 或从已经拥有一些所有权的人那里获得所有权(一些)
这只是常识。
由于所有权份额的销毁是不可逆转的,因此达到零所有者是一个终止事件;到那时,RC就保证不会再被改变,甚至不会被再次测量。 (因此 RC 表示的所有权可以叠加到资源本身的所有权上。)
这些属性使 RC 实现真正的所有权变得非常简单。当弱引用(即 RC-only watcher)进入图片时,情况就不同了:RC 测量工具保证它可以在未来的任何时候对 RC 进行测量,无论是无论用户资源是否仍然存在,受管用户资源是否仍然具有所有者。对于弱引用,原子操作可以将 RC 读取为零,即所有者集合的图形表示中没有竖线。这意味着 RC 的生命周期与用户资源的生命周期不同:RC 本身成为另一个托管资源(通过内部 RC)。
弱引用允许在不共享现有所有权的情况下创建所有者:弱引用是未来所有权的(不可靠的)“选项”。虽然弱引用只能从真实(即强)引用创建,但这与所有权的一般原则相冲突。
所以只有使用弱引用,RC 可以持久为零,即在最后一次减少操作和释放 RC 本身之间的小间隔之外为零。使用弱引用意味着用户代码必须设计为处理零 RC 的可能性,并且在 MT 代码中,一个线程放弃作为最后一个所有者的所有权与另一个试图从弱引用重新创建所有权的线程之间可能存在竞争。参考。
在现实生活中,传统文化知识是免费的,可以随意复制。但是对于极少数人知道的历史传统知识(例如家庭烹饪食谱),您最好在拥有该知识的最后一个人死亡之前复制:在传统知识死亡的人和人之间存在种族获取和传播这些知识。这与弱引用/强引用竞争问题本质上相同。
因此,弱引用对于模拟现实世界中的问题非常有用,其中观察者无法在外部因素破坏资源时强制保持资源存活,但可以在资源存在时观察资源的演变。将弱引用提升为强引用会在转换完成的那一刻对资源的活跃度进行快照,并且本质上是活泼的。
请注意,任何对许多原语的有意义的使用在某种程度上都是不正当的:您使用互斥锁是因为您不知道哪个线程首先需要独占访问资源;如果您知道确切的执行顺序,您将序列化线程并完全避免线程的复杂性。获取资源的竞争不是错误。只有当程序执行的正确性取决于特定的事件顺序时,才会出现错误。
我们知道下面的析构代码应该释放控制
如果这是最后一个 smart_ptr 指向的资源,则阻止
管理。
是的,这是正确的,当 RC 的有用生命周期达到零时结束时的理想行为,即实现真正的所有权并且不支持弱引用。这不对于 Boost 或标准 shared_ptr 是正确的,它确实支持“弱”指针,即非拥有的观察者。
虽然你在语义上提到的竞争条件是不可能的,但这里有问题:
~smart_ptr() {
if (control_block_ptr->refs.fetch_sub(1, memory_order_acq_rel) == 0) {
delete control_block_ptr;
}
}
如前所述,当不存在纯观察者(非拥有且可以看到零 RC)时,RC 的生命周期与托管用户资源相同。我没有看到用户资源 here 的释放(它可能在其他地方)。
用户资源本身在哪里管理?它在*control_block_ptr 对象的析构函数中吗?能不能多发点代码来个全图?
您还使用了 post-递减操作fetch_sub 代替了预递减操作:“post”操作返回前一个值然后执行操作。 在后期操作中,您想要操作的特别有趣的 RC 值,在最后一个所有者不再是所有者之前的值是 1,而不是 0。