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

如何以线程安全的方式使用std::ios_base::pword()?

问题分析与解决方案

原代码是否线程安全?

不安全,核心问题有两点:

  1. 数据竞争导致未定义行为:b.pword(index)返回的void*是非原子类型,第一次读取p = b.pword(index)属于非原子读操作,若其他线程同时在锁内执行b.pword(index) = p(非原子写),两者构成数据竞争,这在C++标准中属于未定义行为。
  2. 内存屏障无效:你添加的std::atomic_thread_fence依赖原子操作才能发挥同步作用,对非原子的指针读写完全起不到预期的内存可见性或原子性保证。第一次读取的p可能是部分写入的无效指针,导致空指针检查不可靠。

修正方案

根据C++版本不同,有三种可靠的实现方式:

方案1:使用C++20 std::atomic_ref(推荐,性能最优)

利用std::atomic_ref将非原子的void*转换为原子操作对象,确保所有对pword指针的读写都是原子的,同时保留双重检查锁的性能优势:

#include <atomic>
#include <ios>
#include <mutex>

class my_obj { /* ... */ };

my_obj* get_obj_for( std::ios_base &b ) {
  static int const index = std::ios_base::xalloc();
  // 将pword返回的void*&包装为原子引用
  std::atomic_ref<void*> p_ref( b.pword( index ) );
  
  void *p = p_ref.load( std::memory_order_acquire );
  if ( p == nullptr ) {
    static std::mutex mutex;
    std::lock_guard<std::mutex> lock{ mutex };
    
    p = p_ref.load( std::memory_order_acquire );
    if ( p == nullptr ) {
      my_obj* new_obj = new my_obj;
      // 用release语义保证my_obj的初始化对其他线程可见
      p_ref.store( new_obj, std::memory_order_release );
      p = new_obj;
    }
  }
  return static_cast<my_obj*>( p );
}

方案2:兼容C++11/17的原子指针包装

如果无法使用C++20,可让pword指向一个std::atomic<my_obj*>,通过原子指针实现线程安全访问:

#include <atomic>
#include <ios>
#include <mutex>

class my_obj { /* ... */ };

my_obj* get_obj_for( std::ios_base &b ) {
  static int const index = std::ios_base::xalloc();
  void* stored_ptr = b.pword( index );
  auto atomic_obj_ptr = static_cast<std::atomic<my_obj*>*>(stored_ptr);
  
  if (atomic_obj_ptr != nullptr) {
    return atomic_obj_ptr->load( std::memory_order_acquire );
  }

  static std::mutex mtx;
  std::lock_guard<std::mutex> lock(mtx);

  stored_ptr = b.pword( index );
  atomic_obj_ptr = static_cast<std::atomic<my_obj*>*>(stored_ptr);
  if (atomic_obj_ptr == nullptr) {
    atomic_obj_ptr = new std::atomic<my_obj*>();
    my_obj* new_obj = new my_obj();
    atomic_obj_ptr->store(new_obj, std::memory_order_release);
    b.pword(index) = atomic_obj_ptr;
  }

  return atomic_obj_ptr->load( std::memory_order_acquire );
}

方案3:简单锁保护(性能一般但实现简单)

如果对性能要求不高,直接用互斥锁保护所有对pword的访问,彻底避免数据竞争:

#include <ios>
#include <mutex>

class my_obj { /* ... */ };

my_obj* get_obj_for( std::ios_base &b ) {
  static int const index = std::ios_base::xalloc();
  static std::mutex mtx;
  std::lock_guard<std::mutex> lock(mtx);
  
  void* p = b.pword(index);
  if (p == nullptr) {
    p = new my_obj;
    b.pword(index) = p;
  }
  return static_cast<my_obj*>(p);
}

内容的提问来源于stack exchange,提问作者Paul J. Lucas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 06:24:59