如果生产者是单线程,依靠“use_count()”重用“shared_ptr”内存是否安全?

问题描述 投票:0回答:2

这与这个未回答的问题有点相似,但稍微具体一些。

在我的应用程序中,我有一个生产者线程,它生成对象以供其他线程使用。可以安全地假设该线程是唯一创建这些对象的线程。

我希望能够在所有其他消费者线程使用完该对象后重用该对象。

最初我认为,如果生产者将保存所有创建的对象,如果它们的引用计数为1,则可以重用它们。但似乎

std::shared_ptr<T>::use_count
可能不够可靠,无法确定这一点,因为它使用的是弱内存访问(为什么?)。

我有一个测试应用程序,我注意到有时

use_count
可能仍然返回 > 1,即使其他消费者已经完成了该对象(或者这可能是我这边的软件错误)。

问题:

  • 在这种情况下,依赖
    use_count
    等于 1 是一种安全的方法吗?
  • 如果不安全,为什么?
  • 此用例有哪些更好的替代方案?
c++ multithreading c++11 thread-safety shared-ptr
2个回答
0
投票

use_count
在多线程场景中不可靠,它不是原子的。确实是超级不靠谱。如果在重用之前绝对有必要将引用计数设置为 1,请研究 std::atomic 共享指针,或专门用于检查引用计数的某种互斥功能。


0
投票

不,这不安全。

use_count
是一个非常有问题的函数,只能用作粗略的近似值。 [util.smartptr.shared.obs]
shared_ptr::use_count
中的标准注释:

当多个线程可能影响

use_count()
的返回值时,结果是近似的。 特别是,
use_count() == 1
并不意味着通过先前销毁的
shared_ptr
进行的访问在任何意义上都已完成。

这些问题是由于缺乏同步造成的。在实践中,问题是:

删除

use_count
中对
unique
shared_ptr
的“仅调试”限制引入了一个错误:为了使
unique
产生有用且可靠的值,它需要一个同步子句来确保先前的访问通过另一个引用对
unique
的成功调用者可见。许多当前的实现使用宽松的负载,并且不提供这种保证,因为标准中没有说明。对于调试/提示使用来说,这是可以的。如果没有它,规范就不清楚且具有误导性。

- P0521:CA 14 的拟议决议 (

shared_ptr
use_count
/
unique
)

您会受到这些问题的影响,即使只有一个线程生成新的

std::shared_ptr
对象,缺乏同步也会阻止您确定地声明唯一所有权。

有什么更好的选择?

在您的情况下,只有一个线程实际生成

std::shared_ptr
对象并增加引用计数。 您还让它听起来像是该线程执行一些后处理,因此永远不会在其他线程之前终止。

这听起来像该线程实际上具有唯一的所有权,并且可以使用

std::unique_ptr
代替。所有其他线程都可以被赋予一个非拥有的原始指针。 要跟踪有多少线程可以访问指针,您可以使用原子计数器,例如
std::atomic_int
,当线程启动/加入时,它会递增/递减(使用
std::memory_order::acq_rel
)。

您还可以使用

std::latch
或其他一些计数机制来跟踪有多少线程放弃了对所指向对象的引用。一旦所有线程都通过闩锁,只有一个线程会访问它。

© www.soinside.com 2019 - 2024. All rights reserved.