You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以线程安全的方式在std::vector中存储std::shared_ptr?

多线程环境下固定大小对象池的轻量无锁实现方案

问题背景

多个线程共享固定大小对象池(初始已知最大容量),部分线程需移除对象,要求对象在无任何线程使用时自动释放。原方案用std::vector<std::shared_ptr<T>>存储,移除时置空指针,但std::atomic<std::shared_ptr<T>>无拷贝构造函数无法直接存入std::vector,只能采用嵌套指针的复杂结构:

struct T {};
std::vector<std::unique_ptr<std::atomic<std::shared_ptr<T>>>> pool;

该方案引入额外内存分配与间接层,代码可读性差,需寻找更简洁、接近无锁的可靠实现。


推荐方案

方案1:用std::array直接存储std::atomic<std::shared_ptr<T>>

由于对象池大小固定,std::array支持聚合初始化,无需拷贝构造元素,可直接存储std::atomic<std::shared_ptr<T>>,完全消除额外间接层:

struct T {};
constexpr size_t MAX_POOL_SIZE = 100;
std::array<std::atomic<std::shared_ptr<T>>, MAX_POOL_SIZE> pool;

// 初始化池
void init_pool() {
    for (size_t i = 0; i < MAX_POOL_SIZE; ++i) {
        pool[i].store(std::make_shared<T>(), std::memory_order_relaxed);
    }
}

// 原子置空标记对象为移除
void remove_object(size_t idx) {
    pool[idx].store(nullptr, std::memory_order_release);
}

// 原子加载获取对象
std::shared_ptr<T> get_object(size_t idx) {
    return pool[idx].load(std::memory_order_acquire);
}

该方案复用标准库组件,代码简洁,完全贴合固定大小池的需求。

方案2:原子引用计数+对象数组(适配多态场景)

若需支持多态类层次,可拆分对象存储与原子引用计数,通过自定义智能指针管理生命周期:

struct Base { virtual ~Base() = default; };
struct Derived : Base {};

constexpr size_t MAX_POOL_SIZE = 100;
std::array<std::unique_ptr<Base>, MAX_POOL_SIZE> objects;
std::array<std::atomic<int>, MAX_POOL_SIZE> ref_counts;

// 初始化池:每个对象初始持有1个池内引用
void init_pool() {
    for (size_t i = 0; i < MAX_POOL_SIZE; ++i) {
        objects[i] = std::make_unique<Derived>();
        ref_counts[i].store(1, std::memory_order_relaxed);
    }
}

// 获取对象:原子递增引用计数,仅当对象未释放时返回
std::unique_ptr<Base, void(*)(Base*)> get_object(size_t idx) {
    int old_count = ref_counts[idx].load(std::memory_order_acquire);
    while (old_count > 0) {
        if (ref_counts[idx].compare_exchange_weak(old_count, old_count + 1, std::memory_order_acq_rel)) {
            // 自定义删除器:释放时原子递减计数,计数为0则销毁对象
            return {objects[idx].get(), [idx](Base* ptr) {
                if (ref_counts[idx].fetch_sub(1, std::memory_order_acq_rel) == 1) {
                    objects[idx].reset();
                }
            }};
        }
    }
    return nullptr; // 对象已被移除
}

// 移除对象:池放弃初始引用
void remove_object(size_t idx) {
    if (ref_counts[idx].fetch_sub(1, std::memory_order_acq_rel) == 1) {
        objects[idx].reset();
    }
}

此方案既支持多态,又通过原子操作实现接近无锁的访问,避免了std::atomic<std::shared_ptr>的容器限制。

方案3:第三方无锁容器(如Boost.Lockfree)

若允许引入第三方库,boost::lockfree::queue可直接存储std::shared_ptr<T>,适合动态取出/放回的场景,但对于固定大小的标记式池,前两种方案更适配。


内容的提问来源于stack exchange,提问作者Damir Tenishev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 04:18:24