关于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
相关产品推荐
相关产品推荐

