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

标签 c++ multithreading c++11 thread-safety shared-ptr

这和这个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 destroyed shared_ptr have in any sense completed.

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

The removal of the "debug only" restriction for use_count and unique in shared_ptr introduced a bug: in order for unique 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 of unique. 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/

相关文章:

c++ - 预先计算的树的树构建函数

c# - 线程化缓慢且不可预测?

java - 多线程的 sleep() 方法和 yield() 方法有什么区别?

c++ - 如果不使用 pthread_join(),为什么从特定线程打印两次 'cout' 语句(即使已同步)?

c++ - Clang 候选模板被忽略 : substitution failure (also fails to compile with gcc, VC 工作正常)

c++ - 'typedef' 是否在 C++ 类中自动继承?

c++ - 提升异步套接字和提升::线程

c++ - Boost.Log 与 Boost.Log v2

c++ - std::move 在 boost 库中的对应物

C++11 通过引用定义函数类型捕获