无需锁定即可从不同线程更改和读取unordered_map的元素

白虎

我在代码中有一个unordered_map,它具有固定数量的在程序启动时初始化的元素

std::unorderd_map<int, bool> flags;
//Initialize all needed values in the map
// Start thread T1
// Start thread T2

所有条目在开始时都初始化为false。有两个线程(T1和T2)。T1可以更改地图中每个条目的值。T2只是定期读取条目。在这种情况下,我需要使用锁吗?在程序的整个生命周期中,unordered_map中的元素总数保持不变。

杰里·科芬

是的,这会引起问题。该标准(第[intro.races] /21.2节)的措辞为:

如果程序的执行包含两个潜在的并发冲突操作,其中至少一个不是原子操作,并且没有一个先于另一个发生,则执行该程序将导致数据争用,除了以下所述的信号处理程序的特殊情况。任何此类数据争用都会导致未定义的行为。

因此,如果仅从多个线程读取,则不会出现“冲突的操作”,但是,即使有一个线程写入,也需要同步访问。

在的特定情况下unordered_map,我可以看到同时进行读写操作可能会导致严重问题的情况。

unordered_map使用具有冲突链的哈希表。这意味着,如果两个(或更多)键散列为相同的值,则它们都将存储在相同的“存储桶”中,该存储桶基本上是这些值的链接列表。因此,在中查找内容unordered_map可能涉及遍历链表。

经验表明,在典型情况下,我们通常可以期望集合中的某些元素比其他元素更频繁地访问,通常遵循类似众所周知的80:20规则(20%的元素将占访问的80%) )。

在这种情况下,优化哈希表的一种方法是尝试将最常访问的元素保持在其各自链接列表的开头。为此,写一个值不仅可以修改值本身,而且可以将其节点移到(或移向)链表的开头。因此,您的阅读线程可能会尝试遍历链表,就像写线程正在尝试将链表中的节点向前移动一样,因此,链表当时已完全损坏(例如,包含next未尚未初始化)。

因此,即使您不进行任何插入删除操作,并将映射中的值从更改boolstd::atomic<bool>,您仍然有潜在的竞争条件。您确实需要使用锁来保护对地图本身的访问。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章