这和这个unanswered question有点相似,但稍微具体一些。
在我的应用程序中,我有一个生产者线程,它生成对象以供其他线程使用。可以安全地假设该线程是唯一创建这些对象的线程。
我希望能够在所有其他消费者线程使用完该对象后重用该对象。
最初我以为生产者会保存所有创建的对象,如果它们的引用计数为1,它就可以重用它们。但似乎std::shared_ptr<T>::use_count
可能不够可靠,无法确定这一点,因为它使用的是弱内存访问(为什么?)。
我有一个测试应用程序,我注意到有时 use_count
即使其他消费者已经完成了该对象,仍然可能返回> 1(或者这可能是我这边的软件错误)。
问题:
- 是否依赖
use_count
在这种情况下等于 1 是一种安全的方法吗? - 如果不安全,为什么?
- 对于此用例,有哪些更好的替代方案?
最佳答案
不,这不安全。 use_count
是一个非常有问题的函数,只能用作粗略的近似。标准注释在 [util.smartptr.shared.obs] shared_ptr::use_count
:
When multiple threads might affect the return value of
use_count()
, the result is approximate. In particular,use_count() == 1
does not imply that accesses through a previously destroyedshared_ptr
have in any sense completed.
这些问题是由于缺乏同步造成的。在实践中,问题是:
The removal of the "debug only" restriction for
use_count
andunique
inshared_ptr
introduced a bug: in order forunique
to produce a useful and reliable value, it needs a synchronize clause to ensure that prior accesses through another reference are visible to the successful caller ofunique
. Many current implementations use a relaxed load, and do not provide this guarantee, since it’s not stated in the Standard. For debug/hint usage that was OK. Without it the specification is unclear and misleading.
-P0521: Proposed Resolution for CA 14 ( shared_ptr
use_count
/ unique
)
注意:曾经有一个 unique()
返回 use_count() == 1
的函数,因具有误导性而被删除。
即使只有一个线程产生新的std::shared_ptr
,您也会受到这些问题的影响。对象,缺乏同步将阻止您确定地声明唯一所有权。
有哪些更好的选择?
在您的情况下,实际上只有一个线程生成 std::shared_ptr
对象并增加引用计数。
您还使它听起来像是该线程执行一些后处理,因此永远不会在其他线程之前终止。
这听起来好像该线程实际上具有唯一的所有权,并且可以使用 std::unique_ptr
反而。所有其他线程都可以被赋予一个非拥有的原始指针。
要跟踪有多少线程可以访问该指针,您可以使用原子计数器,例如 std::atomic_int
当线程启动/加入时,它会增加/减少(使用 std::memory_order::acq_rel
)。
您还可以使用 std::latch
或其他一些计数机制来跟踪有多少线程放弃了对所指向对象的引用。一旦所有线程都通过闩锁,只有一个线程会访问它。
关于c++ - 如果生产者是单线程,依靠 "use_count()"重用 "shared_ptr"内存是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77286007/