std::map<int, std::bitset<256 > > 线程安全 w/o 互斥?

std::map<int, std::bitset<256 > > thread safety w/o mutex?

我有一个

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);

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

不,这不安全。

bitset

operator= 是一个修改操作,因此如果 bitset 在另一个线程中同时访问,则不能保证没有数据竞争。实际上,它几乎肯定会导致数据竞争,因为它需要写入对象。这并非特定于 std::bitset,但几乎所有 non-empty non-atomic 标准库类型(以及大多数其他 non-empty 类型)通常都是如此。

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

如果您希望能够执行此操作,请将 bitset 包装在 structmutex 中以保护对 bitset 的访问,或者使用类似于 atomic shared_ptr 的东西,允许以原子方式将旧 bitset 与新 bitset 交换,并延迟销毁旧 shared_ptr 直到所有引用都消失。

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

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