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

问题描述 投票:0回答:1

我想使用

pword()
来存储任何流的附加对象,但是以线程安全的方式,特别是在第一次为给定流分配 对象的一个 实例。由于
void*
所指的
pword()
不是原子的,因此不能使用比较交换函数,我相信我必须采用带有内存屏障的双重检查锁定。以this为起点,我有:

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

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

然而,在that文章给出的“使用C++11获取和释放栅栏”示例中,其指针

m_instance
std::atomic
。即使
void*
所指的
pword()
not
std::atomic
,我上面的代码仍然是线程安全的吗?如果不是,有什么办法让代码线程安全吗?

c++ multithreading thread-safety iostream
1个回答
0
投票

你的权利,

void *p = b.pword( index );
处指针的非原子读取相对于
b.pword( index ) = p;
处的赋值来说不是线程安全的。

您可以尝试使用

std::atomic_ref<void*>(b.pword(index));
来解决此问题,但您会遇到
pword()
本身不是线程安全的问题。

此外,

pword()
的结果可能会因流中的任何操作而无效,因此如果在任何其他线程中同时调用流上的任何操作,则
b.pword(index) = p
可能会无效。

必须锁定

pword()
。并且您必须对流上的所有操作使用相同的锁。

使用地图可能会更容易:

my_obj* get_obj_for( std::ios_base &b ) {
    static std::unordered_map<std::ios_base*, my_obj> map;
    static std::shared_mutex m;
    {
        std::shared_lock _(m);
        auto it = map.find(&b);
        if (it != map.end()) return &it->second;
    }
    {
        std::unique_lock _(m);
        return &map[&b];
    }
}

但是请确保进入该映射的指针不会消失。如果新蒸汽重复使用该地址,您将重复使用关联的

my_obj


还要考虑重新设计你想做的事情。您可以将一个流包装在另一个包含

my_obj
的流中,并使用包装后的流,例如
std::osyncstream

© www.soinside.com 2019 - 2024. All rights reserved.