C++ stl unordered_map 单写(只插入,不删除)和单读并发访问

Posted

技术标签:

【中文标题】C++ stl unordered_map 单写(只插入,不删除)和单读并发访问【英文标题】:C++ stl unordered_map single writer(only insert, no delete) and single reader concurrent access 【发布时间】:2021-11-27 10:17:26 【问题描述】:

我有一个如下的数据结构:

struct Count 
uint64_t rcvd;
uint64_t sent;
 ;
std::unordered_map<int, Count> sCount;

Thread1- Writer(仅 stl 无序映射插入操作),Thread2- Reader(总是通过 const 迭代器访问,例如 const begin() 到 const end())

这个操作是线程安全的吗? 我对这个文档的阅读 - https://en.cppreference.com/w/cpp/container ('Iterator invalidation' 和 'Thread safety') 表明我的并发操作是线程安全的,因为迭代器永远不会失效。但是我在任何地方都没有看到明确的提及(与单个插入、单个 const 迭代器有关),并且 *** 中的其他相关问题告诉我在插入之前始终使用锁。我正在执行一个性能关键的操作,我不能使用锁。

编辑:在没有无锁选项的情况下,我最好的选择是什么?我正在寻找一个偶尔的插入操作(快速且非阻塞)和一个缓慢但更频繁的阅读器(常量迭代器)?

【问题讨论】:

"[...] 因为迭代器永远不会失效。" 实际上这不是真的。插入新元素时可能会发生重新散列。如果发生这种情况,所有以前使用的迭代器都会失效。 @Fareanor 您的声明有参考资料吗?从问题中的链接(迭代器有效性的表列表)中,它清楚地表明迭代器在插入后有效。您的意思是说该文件有误吗? 你在哪里看到的? linked reference 明确提到了我所说的(查看表中的“有条件”列)。如果您想要第二个证明,here 您可以看到确切的声明“如果由于插入而发生重新散列,则所有迭代器都将失效。” 【参考方案1】:

一个线程写入对象而另一个线程读取该对象通常是未定义的行为。您要么需要显式同步,要么需要使用 std::atomic 对象。

关于容器的线程安全保证没有提到插入,因为插入不是线程安全的。

容器线程安全保证告诉您std::unordered_map 中没有mutablestatic 数据成员,也没有任何具有static 存储持续时间的对象被成员函数修改。

当一个表达式的计算写入一个内存位置而另一个计算读取或修改相同的内存位置时,表达式被称为冲突。具有两个冲突评估的程序存在数据竞争,除非

两个评估都在同一个线程或同一个信号处理程序中执行,或者 两个冲突的计算都是原子操作(参见 std::atomic),或者 其中一个冲突的评估发生在另一个之前(参见 std::memory_order)

如果发生数据竞争,程序的行为是不确定的。

【讨论】:

我的问题非常具体到 unordered_map 'insert' 操作。如何使用'std::atomic' 和 unordered_map。顺便说一句,我并不担心存储的实际值,而是 std::unordered_map 本身的并发方面。 @deadbeef 你有一个std::atomic&lt;std::unordered_map&lt;key, value&gt;&gt; 而不是std::unordered_map&lt;key, value&gt;

以上是关于C++ stl unordered_map 单写(只插入,不删除)和单读并发访问的主要内容,如果未能解决你的问题,请参考以下文章

c++ stl unordered_map 如何打印其所有值?

C++ STL 相关 部分笔记

C++源码剖析——unordered_map和unordered_set

二进制搜索 C++ STL

C++ STL常用容器以及操作简介

(转载)STL map与Boost unordered_map的比较