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
,我上面的代码仍然是线程安全的吗?如果不是,有什么办法让代码线程安全吗?
你的权利,
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
。