std::map<int, std::bitset<256 > > 线程安全没有互斥锁?



我有一个

std::map<int, std::bitset<256 > > m;

施工后,不会插入任何新钥匙,也不会移除任何钥匙。是否可以在一个线程中安全地分配位集,同时在其他线程中读取位集,而无需使用互斥锁?

// thread 1: 
std::bitset<256> a = getBitset();
m[1] = a;  
// thread 2:
std::bitset<256> b = m[1]; 
// or alternatively 
std::bitset<256> c = m.at(1);

我认为程序不会崩溃,但位集中可能会发生数据竞争。如果读取将提供旧位集和新位集的组合,则数据争用是可以接受的。

不,这不安全。

bitsetoperator=是一个修改操作,因此,如果在另一个线程中同时访问bitset,则不能保证没有数据争用。在实践中,它几乎肯定会引起数据竞赛,因为它需要写入对象。这不是特定于std::bitset,但通常适用于所有非空的非原子标准库类型(以及大多数其他非空类型)。

任何数据争用都将导致未定义的行为。没有部分更新。从这个意义上说,它永远不应该被接受。

如果您希望能够做到这一点,请将bitset包裹在带有mutexstruct中以保护对bitset的访问,或者使用类似原子shared_ptr的东西,允许以原子方式将旧bitset与新交换,并延迟旧的销毁,直到所有引用都消失。

虽然我认为不能保证,但std::bitset也可能是微不足道的可复制的。您可以使用std::is_trivially_copyable_v上的static_assert进行检查,如果true,您还可以使用允许原子访问位集的std::atomicstd::atomic_ref(即没有数据竞争),并且可能仅在底层架构不支持对相应大小的对象进行原子访问时才使用锁。

另请注意,std::bitset b = m[1];m[1]也会导致未定义的行为,因为std::mapoperator[]也是一个修改操作,并且未指定为没有数据争用,即使它没有在映射中插入新元素也是如此。例如,您需要改用find()成员函数。