这与这个未回答的问题有点相似,但稍微具体一些。
在我的应用程序中,我有一个生产者线程,它生成对象以供其他线程使用。可以安全地假设该线程是唯一创建这些对象的线程。
我希望能够在所有其他消费者线程使用完该对象后重用该对象。
最初我认为,如果生产者将保存所有创建的对象,如果它们的引用计数为1,则可以重用它们。但似乎
std::shared_ptr<T>::use_count
可能不够可靠,无法确定这一点,因为它使用的是弱内存访问(为什么?)。
我有一个测试应用程序,我注意到有时
use_count
可能仍然返回 > 1,即使其他消费者已经完成了该对象(或者这可能是我这边的软件错误)。
问题:
use_count
等于 1 是一种安全的方法吗?use_count
在多线程场景中不可靠,它不是原子的。确实是超级不靠谱。如果在重用之前绝对有必要将引用计数设置为 1,请研究 std::atomic 共享指针,或专门用于检查引用计数的某种互斥功能。
不,这不安全。
use_count
是一个非常有问题的函数,只能用作粗略的近似值。 [util.smartptr.shared.obs] shared_ptr::use_count
中的标准注释:
当多个线程可能影响
的返回值时,结果是近似的。 特别是,use_count()
并不意味着通过先前销毁的use_count() == 1
进行的访问在任何意义上都已完成。shared_ptr
这些问题是由于缺乏同步造成的。在实践中,问题是:
删除
中对use_count
和unique
的“仅调试”限制引入了一个错误:为了使shared_ptr
产生有用且可靠的值,它需要一个同步子句来确保先前的访问通过另一个引用对unique
的成功调用者可见。许多当前的实现使用宽松的负载,并且不提供这种保证,因为标准中没有说明。对于调试/提示使用来说,这是可以的。如果没有它,规范就不清楚且具有误导性。unique
shared_ptr
use_count
/unique
)
您会受到这些问题的影响,即使只有一个线程生成新的
std::shared_ptr
对象,缺乏同步也会阻止您确定地声明唯一所有权。
在您的情况下,只有一个线程实际生成
std::shared_ptr
对象并增加引用计数。
您还让它听起来像是该线程执行一些后处理,因此永远不会在其他线程之前终止。
这听起来像该线程实际上具有唯一的所有权,并且可以使用
std::unique_ptr
代替。所有其他线程都可以被赋予一个非拥有的原始指针。
要跟踪有多少线程可以访问指针,您可以使用原子计数器,例如 std::atomic_int
,当线程启动/加入时,它会递增/递减(使用 std::memory_order::acq_rel
)。
std::latch
或其他一些计数机制来跟踪有多少线程放弃了对所指向对象的引用。一旦所有线程都通过闩锁,只有一个线程会访问它。