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

关于std::shared_ptr内存序与断言是否可能失败的技术咨询

关于std::shared_ptr内存序与断言失败的分析

已知std::shared_ptr的典型推荐实现规则:

  • 计数器递增采用std::memory_order::relaxed
  • 计数器递减操作使用std::memory_order::release
  • 若为最后一个引用,删除控制块前会执行std::memory_order::acquire内存栅栏

待分析代码

#include <atomic>
#include <thread>
#include <cassert>
#include <memory>

std::atomic_bool ab{false};

int main()
{
    auto iPtr = std::make_shared<int>(5);
    std::thread thr{[iPtr] // 此处对应 counter.fetch_add(1, std::memory_order::relaxed)
    {
        while (!ab.load(std::memory_order::relaxed));
        assert(*iPtr == 6); // B

        // 析构时执行 counter.fetch_sub(1, std::memory_order_release),随后执行 std::atomic_thread_fence(std::memory_order_acquire)
    }};

    *iPtr = 6; // A
    iPtr.reset(); // 此处对应 counter.fetch_sub(1, std::memory_order_release)

    ab.store(true, std::memory_order::relaxed);
    
    thr.join();
}

问题解答

你的判断是正确的,assert(*iPtr == 6)确实有可能失败,原因如下:

  • 主线程中,操作A(*iPtr = 6)发生在iPtr.reset()的release操作之前,release能保证A的写入不会被重排到release之后,但release操作需要配对的acquire操作才能建立跨线程的同步关系,否则其他线程无法确保看到A的修改。
  • 子线程在执行断言B之前,没有任何acquire操作:子线程对ab的load和主线程对ab的store都用了relaxed内存序,不会形成同步;而子线程中shared_ptr的析构操作(包含acquire栅栏)是在断言之后执行的,对断言前的内存可见性没有帮助。
  • 由于缺少配对的acquire操作,操作A和操作B之间不存在“先行发生”的同步关系,子线程可能读取到*iPtr的旧值(5),导致断言失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:33:12