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

C++原子操作实现shared_ptr/weak_ptr的内存顺序与线程栅栏疑问

线程安全shared_ptr<T>与weak_ptr<T>实现的内存顺序及测试疑问

学习std::atomic与std::memory_order后,我参考微软博客《Inside STL: Smart pointers》尝试实现线程安全的shared_ptr<T>与weak_ptr<T>,但对以下内容存在疑问:

实现代码

控制块

class control_block {
  public:
    constexpr control_block() : strong(1), weak(1) {}

    void inc_strong() { strong.fetch_add(1, std::memory_order_relaxed); }
    void inc_weak() { weak.fetch_add(1, std::memory_order_relaxed); }
    int32_t dec_strong() { return strong.fetch_sub(1, std::memory_order_release); }
    int32_t dec_weak() { return weak.fetch_sub(1, std::memory_order_release); }
    int32_t use_count() { return strong.load(std::memory_order_relaxed); }

  public:
    std::atomic<int32_t> strong;
    std::atomic<int32_t> weak;
};

shared_ptr<T>实现

template<class T>
class shared_ptr {
  public:
    using element_type = T;
    using weak_type = weak_ptr<T>;

    friend class weak_ptr<T>;
  public:
    constexpr shared_ptr() noexcept: object(nullptr), blk(nullptr) {}

    explicit shared_ptr(T *ptr) : object(ptr), blk(new control_block()) {}

    shared_ptr(const shared_ptr &other) noexcept {
        object = other.object;
        blk = other.blk;
        if (blk) {
            blk->inc_strong();
        }
    }

    shared_ptr(shared_ptr &&other) noexcept {
        object = other.object;
        blk = other.blk;
        other.object = nullptr;
        other.blk = nullptr;
    }

    shared_ptr<T> &operator=(const shared_ptr &other) noexcept {
        if (this == &other) return *this;
        release_strong_ref();
        object = other.object;
        blk = other.blk;
        if (blk) {
            blk->inc_strong();
        }
        return *this;
    };

    shared_ptr<T> &operator=(shared_ptr &&other) noexcept {
        if (this == &other) return *this;
        object = other.object;
        blk = other.blk;
        other.object = nullptr;
        other.blk = nullptr;
        return *this;
    };

    ~shared_ptr() {
        release_strong_ref();
    }
  public:
    explicit operator bool() const noexcept { return blk != nullptr; }

    [[nodiscard]] element_type *get() const noexcept { return object; }
    [[nodiscard]] int32_t use_count() const noexcept { return blk ? blk->use_count() : 0; }
    T &operator*() const noexcept { return *object; }
    T *operator->() const noexcept { return object; }

  private:
    void release_strong_ref() noexcept {
        if (!blk) return;
        if (blk->dec_strong() == 1) {
            std::atomic_thread_fence(std::memory_order_acquire);
            delete object;
            if (blk->dec_weak() == 1) {
                std::atomic_thread_fence(std::memory_order_acquire);
                delete blk;
            }
        }
    }

  private:
    T *object;
    control_block *blk;
};

weak_ptr<T>实现

template<class T>
class weak_ptr {
  public:
    using element_type = T;

    friend class shared_ptr<T>;
  public:
    constexpr weak_ptr() noexcept: object(nullptr), blk(nullptr) {};

    weak_ptr(const weak_ptr &other) noexcept {
        release_weak_ref();
        object = other.object;
        blk = other.blk;
        if (blk) {
            blk->inc_weak();
        }
    }

    weak_ptr(weak_ptr &&other) noexcept {
        object = other.object;
        blk = other.blk;
        other.object = nullptr;
        other.blk = nullptr;
    }

    template<class Y>
    weak_ptr(const shared_ptr<Y> &other) noexcept {
        if (!other) {
            object = nullptr;
            blk = nullptr;
            return;
        }
        object = other.object;
        blk = other.blk;
        blk->inc_weak();
    }

    ~weak_ptr() {
        release_weak_ref();
    }

    template<class Y>
    weak_ptr<T> &operator=(const shared_ptr<Y> &other) noexcept {
        release_weak_ref();
        if (!other) {
            object = nullptr;
            blk = nullptr;
            return *this;
        }
        object = other.object;
        blk = other.blk;
        blk->inc_weak();
        return *this;
    };

    weak_ptr<T> &operator=(const weak_ptr &other) noexcept {
        if (this == &other) return *this;
        release_weak_ref();
        object = other.object;
        blk = other.blk;
        if (blk) {
            blk->inc_weak();
        }
        return *this;
    };

    weak_ptr<T> &operator=(weak_ptr &&other) noexcept {
        if (this == &other) return *this;
        object = other.object;
        blk = other.blk;
        other.object = nullptr;
        other.blk = nullptr;
        return *this;
    };

  public:
    explicit operator bool() const noexcept { return blk != nullptr; }

    shared_ptr<T> lock() noexcept {
        if (!blk) return shared_ptr<T>();
        int32_t old = blk->strong.load(std::memory_order_relaxed);
        while (old > 0) {
            if (blk->strong.compare_exchange_weak(old, old + 1, std::memory_order_acquire)) {
                shared_ptr<T> ptr;
                ptr.object = object;
                ptr.blk = blk;
                return ptr;
            }
            old = blk->strong.load(std::memory_order_relaxed);
        }
        return shared_ptr<T>();
    }

    [[nodiscard]]int32_t use_count() const noexcept { return blk ? blk->use_count() : 0; }
    [[nodiscard]]bool expired() const noexcept { return use_count() == 0; }

  private:
    void release_weak_ref() noexcept {
        if (!blk) return;
        if (blk->dec_weak() == 1) {
            std::atomic_thread_fence(std::memory_order_acquire);
            delete blk;
        }
    }

  private:
    T *object;
    control_block *blk;
};

疑问解答

1. lock()中CAS循环的内存顺序

  • 成功时:使用std::memory_order_acquire。CAS成功递增强引用后,需要确保后续对object的访问能看到其他线程对该对象的所有修改——其他线程释放强引用时的release操作会同步这些修改,acquire语义会阻止后续内存操作重排到CAS之前,同时保证可见性。
  • 失败时:使用std::memory_order_relaxed即可。失败仅需重新加载强引用的最新值,无需同步语义,因为CAS失败意味着值已被其他线程修改,我们只需要获取新值重试,不需要建立内存可见性。

优化后的CAS调用应为:

if (blk->strong.compare_exchange_weak(old, old + 1, std::memory_order_acquire, std::memory_order_relaxed))

2. 析构中std::atomic_thread_fence的内存顺序是否正确

是正确的,理由如下:

  • 当dec_strong()返回1时,当前线程是最后一个持有强引用的线程。dec_strong()用memory_order_release会同步当前线程对object的所有写入;后续的acquire栅栏会阻止delete object重排到栅栏之前,同时保证能看到其他线程对object的最终写入,确保安全删除。
  • 同理,dec_weak()返回1时,当前线程是最后一个持有弱引用的线程,acquire栅栏确保控制块的所有操作完成后再删除控制块,避免悬空指针。

3. 跨平台线程安全测试方法

仅在x86平台测试无法暴露弱内存模型下的问题,可通过以下方式全面验证:

  • 弱内存模型平台验证:使用ARM、PowerPC等弱内存模型硬件,或用QEMU模拟这类平台运行测试程序,触发内存重排相关问题。
  • 压力测试:编写多线程用例,让大量线程同时执行shared_ptr的创建、拷贝、移动、析构,以及weak_ptr的lock、析构等操作,长时间运行后检查崩溃、内存泄漏或数据损坏情况。
  • 工具检测:使用ThreadSanitizer(TSAN)这类线程安全检测工具,它能自动检测数据竞争、内存顺序错误,支持GCC、Clang等编译器,无需依赖特定硬件。
  • 边界场景测试:构造极端场景验证:
    • 最后一个强引用释放时,多个weak_ptr并发调用lock;
    • 多线程同时拷贝同一个shared_ptr;
    • weak_ptr在关联对象销毁后尝试lock;
    • 频繁创建销毁智能指针,验证引用计数的正确性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 14:14:56