依赖“use_count()”是否安全?重用“shared_ptr”多线程应用程序中的内存?

发布于 2025-01-14 08:38:50 字数 1769 浏览 1 评论 0原文

我正在审查一些旧代码,我对这段代码的可靠性表示怀疑。不幸的是,原来的开发者已经不在了,所以我无法向他寻求解释。

下面的类负责根据需要为struct分配内存,而不是在多个地方和多个线程中使用。 getStruct() 仅从一处和一个线程调用,但返回的 std::shared_ptr 会分派到多个线程。作为一种优化,我们尝试重用之前创建的 struct,而不是丢弃旧的。

#include <vector>
#include <memory>
#include <algorithm>

struct SomeHeavyStruct
{};

class HeavyStructProvider
{
public:
    std::shared_ptr<SomeHeavyStruct> getStruct()
    {
        auto it =
            std::find_if(m_buffer.cbegin(), m_buffer.cend(), [&](const auto& pointer) {
                return pointer.use_count() == 1;
            });

        if (it != m_buffer.cend()) {
            auto pointer = *it;
            return pointer;
        }

        auto newStruct = std::make_shared<SomeHeavyStruct>();
        m_buffer.push_back(newStruct);
        return newStruct;
    }

private:
    std::vector<std::shared_ptr<SomeHeavyStruct>> m_buffer;
};

我担心的是,在阅读 std::shared_ptr时: :use_count()文档,我看到以下内容:

在多线程环境中,use_count 返回的值是近似值(典型实现使用 memory_order_relaxed 加载)

并且:

如果 use_count 返回 1,则没有其他所有者。 [...] 在多线程环境中,这并不意味着该对象可以安全地修改,因为以前的共享所有者对托管对象的访问可能尚未完成,并且因为新的共享所有者可能会同时引入,例如通过 std: :weak_ptr::锁。

知道 std::shared_ptr是从多个线程使用的,我是否正确地假设当前实现容易出现意外行为?

如果我必须重构它代码,建议的方法是什么?使用受 mutex 保护的自定义 Deleter 是一种可行的方法(使用自定义标志有效地标记可重用结构,而不是依赖 use_count()< /代码>)?

I'm reviewing some old code and I have doubt about the reliability of this one. Unfortunately, the original developer is no longer around so I can't ask him for an explanation.

The following class is in charge of allocating memory on demand for an struct than is used in multiple places and from multiple threads. The getStruct() is called from one place and one thread only, but the returned std::shared_ptr<SomeHeavyStruct> is dispatched to multiple threads. As an optimization, an attempt is made to try to re-use previously created struct instead of throwing away old ones.

#include <vector>
#include <memory>
#include <algorithm>

struct SomeHeavyStruct
{};

class HeavyStructProvider
{
public:
    std::shared_ptr<SomeHeavyStruct> getStruct()
    {
        auto it =
            std::find_if(m_buffer.cbegin(), m_buffer.cend(), [&](const auto& pointer) {
                return pointer.use_count() == 1;
            });

        if (it != m_buffer.cend()) {
            auto pointer = *it;
            return pointer;
        }

        auto newStruct = std::make_shared<SomeHeavyStruct>();
        m_buffer.push_back(newStruct);
        return newStruct;
    }

private:
    std::vector<std::shared_ptr<SomeHeavyStruct>> m_buffer;
};

My concern is that while reading the std::shared_ptr<T>::use_count() documentation, I'm seeing the following:

In multithreaded environment, the value returned by use_count is approximate (typical implementations use a memory_order_relaxed load)

And also:

If use_count returns 1, there are no other owners. [...] In multithreaded environment, this does not imply that the object is safe to modify because accesses to the managed object by former shared owners may not have completed, and because new shared owners may be introduced concurrently, such as by std::weak_ptr::lock.

Knowing that the std::shared_ptr<SomeHeavyStruct> is used from multiple threads, am I right to assume the current implementation is prone to unexpected behavior?

If I had to refactor this code, what would be a suggested approach? Is using a custom Deleter protected by a mutex the way to go (to effectively mark re-usable struct with a custom flag instead of relying on use_count())?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文