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

多线程无锁访问shared_ptr时operator=正常但reset触发coredump原因问询

问题根因分析

核心前提:你的代码本身存在未定义行为

多个线程并发读写同一个非原子std::shared_ptr对象本身就是数据竞争,属于C++标准定义的未定义行为,无论用operator=还是reset都不是合规的写法,operator=运行正常仅为编译器实现带来的巧合,不代表代码是安全的。

两种操作的执行顺序差异(以libstdc++实现为例)

崩溃的核心原因是reset和operator=对旧资源的释放时机完全相反:

  • string_ptr = std::make_shared<...>的执行顺序

    1. 先执行std::make_shared构造新的字符串对象和对应控制块,生成一个临时shared_ptr,新对象引用计数为1
    2. 赋值操作优先完成内部指针/控制块的替换:将string_ptr的内部指向切换为新的临时对象的内容
    3. 最后才对原来的旧对象引用计数减1,若计数归零则释放旧对象
      这种场景下,哪怕读线程在替换瞬间获取指针,要么拿到未释放的旧指针,要么拿到新指针,刚好命中释放后访问的概率极低,所以你测试时没有触发崩溃。
  • string_ptr.reset(new ...)的执行顺序

    1. 先执行new std::string(...)构造新的字符串对象,拿到裸指针
    2. 进入reset逻辑后,先对string_ptr持有的旧对象引用计数减1,若计数归零则立刻释放旧对象
    3. 最后才将string_ptr的内部指针替换为新的裸指针,绑定新的控制块
      这里存在非常明显的空窗期:步骤2到步骤3之间,string_ptr内部持有的是已经被释放的野指针,此时读线程如果刚好调用get()获取指针并解引用,就会直接触发野内存访问,导致coredump,这也是你可以稳定复现崩溃的原因。

正确的多线程使用方案

如果要并发读写shared_ptr,请选择以下任意一种合规方案:

  • 所有对shared_ptr的读写操作都加互斥锁保护
  • 使用C++20引入的std::atomic_shared_ptr,原子读写操作是线程安全的
  • 读线程优先拷贝生成局部shared_ptr副本再使用(注意拷贝操作本身也需要和写操作同步,否则依然是数据竞争)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 00:24:00