【问题标题】:C++: How to achieve unique ownership but non-unique thread-safe weak accessC++:如何实现唯一所有权但非唯一线程安全弱访问
【发布时间】:2021-05-23 13:58:17
【问题描述】:

我有一个类Inner,它应该唯一归类Outer所有,具体来说,除了Outer之外,没有人应该能够摧毁Inner,或者让它活着。

但是,我希望其他类 Consumers,可能存在于其他线程上,能够引用 Inner 并与它交互,只要它存在,当它最终死亡时,这些其他类只知道停止与Inner 互动。

换句话说,我想要Inner 的唯一所有权,但非唯一的生命周期限制访问。

理论上,这里的完美解决方案似乎是:

class Inner {
  ...
};

class Outer {
  std::unique_ptr<Inner> unique_inner_;
  std::weak_ptr<Inner> GetWeakInnerForConsumers() {...}
};

class Consumer {
  Consumer(std::weak_ptr<Inner> weak_inner) : weak_inner_(weak_inner) {}

  std::weak_ptr<Inner> weak_inner_;
};

但是这里的问题是你不能从 std::unique_ptr 创建一个弱指针,所以我不走运。

另一种方法是:

  • Inner 继承自std::enable_shared_from_this&lt;Inner&gt; 甚至 尽管Inner 的唯一实例归std::unique_ptr 所有
  • ...并介绍std::weak_ptr&lt;Inner&gt; GetWeakPtr() {return std::weak_ptr&lt;Inner&gt;(shared_from_this())}

...但是上面的方法似乎不起作用,因为shared_from_this() 似乎在Inner 是唯一拥有的时候抛出异常,这是完全合理的。即使它确实有效,我认为它也可能容易受到竞争的影响,因为weak_ptr::lock() 显然不能保持unique_ptr 活着。

所以考虑到这一切,我只能想出两个解决方案:

  • Outer 维护一个std::shared_ptr&lt;Inner&gt;,并记录一下尽管有成员,Outer 实际上是唯一的所有者这一事实,并确保没有人获得对Inner 的强烈引用。这似乎是获胜的选择。或者...
  • 代替上面的,分发原始的Inner*指向所有Consumers的指针,但是Consumer继承自class Inner::Observer,所以Consumer可以观察Inner的破坏,并保持一些状态以便它知道其到 Inner* 的原始 ptr 何时“过时”。这似乎没问题,但可能容易受到原始 ptr 访问和破坏观察之间的竞争的影响

上面的第一个选择是我唯一的选择吗?是否有其他方法可以实现唯一所有权但非唯一线程安全的弱访问?

【问题讨论】:

  • 你的问题是不是无法解决?如果一个线程拥有销毁对象的不受约束的权利,那么另一个线程可能会检查该对象是否处于活动状态,开始对其进行处理,但在此过程中,该对象可能会死亡,并且您将获得 UB。跨度>
  • 我也是这么想的。我想如果由于弱指针需要是线程安全的,它可以在使用时 暂时 保持 unique_ptr 活动,那将是理想的,但这不是真实存在的东西,也不应该它...我真的只是想防止分发对Inner 的长期强引用,但我可以分发weak_ptrs,它可以在使用它的短期内暂时保持底层对象的活动状态,但我认为不存在这样的概念:)
  • 一旦您在weak_ptr 上调用lock,您就请求拥有该对象的所有权,如果不请求所有权,您就不能使用weak_prt 所引用的对象。所以到那时Outer 不能再成为唯一的所有者了。
  • 我知道,看我上面的评论。我认为我的问题格式不正确
  • that can temporarily keep the underlying object alive throughout the short-term period 好吧,这仍然意味着它在该时间段内请求所有权(以使其保持活力)。这就是所有权的意义所在。

标签: c++ shared-ptr unique-ptr lifetime weak-ptr


【解决方案1】:

根据在 cmets 中与 Brian Bi 的一些讨论,我得出的结论是,这实际上是一个无法解决的问题。如果您希望weak_ptr 是真正的线程安全的,那么它必须对底层对象具有临时所有权,在这种情况下,它不能是unique_ptr 的weak_ptr,显然

我真正想要的是保证我永远不会向 Inner 发送任何长期存储的强引用,这样就没有其他对象可以有意义地保持 Inner 存活一段时间一段的时间。我可以分发 std::weak_ptr&lt;Inners&gt;s,但他们只会在访问期间暂时保持底层 Inner 处于活动状态,在这种情况下,我只需要实施以下保护措施:

  • 确保我永远不会将std::shared_ptr&lt;Inner&gt; 分发给不是Outer 的任何人
  • 确保std::weak_ptr&lt;Inner&gt; 的所有消费者仅通过.lock() 生产std::shared_ptr&lt;Inner&gt;s 并临时使用它们,切勿“长期”存储它们

无论如何,我学到了一些东西并深入研究了奇怪的所有权问题:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-21
    • 2019-08-08
    • 1970-01-01
    相关资源
    最近更新 更多