std::map的高效线程同步技术

Efficient thread synchronization technique for std::map

本文关键字:同步 技术 线程 map std 高效      更新时间:2023-10-16

我有std::map< StudentName, Marks >,其中StudentNamestd::stringMarks是整数。

现在,在我的应用程序中,多个线程正在访问这个映射到:

  1. 查找StudentName。如果存在,则增加其Marks
  2. 降低StudentNameMarks
  3. StudentName添加到地图中
  4. 从地图中删除StudentName

问题:在多线程环境中,对std::map执行上述操作最有效的方法是什么?

当前解决方案:在地图上执行所有这些操作的代码都放在关键部分中。但这会降低性能
(例如,如果一个线程正在为特定学生添加分数,为什么其他想要为不同学生添加分数的线程需要等待?)

我认为这是可以做到的:
我从SO上的其他类似问题/答案中收集了关于映射上多线程的信息,以下是我认为我需要做的。假设std::map不是线程安全的(即,当映射更新时,没有其他线程应该访问它)

  1. 我只想将最后两个(添加/删除StudentName)活动排除在外(在映射中添加/删除元素时,不应并行执行其他活动)
  2. 不允许多个线程访问映射的同一元素(这样多个线程就不能同时尝试增加/减少同一学生的分数)

但我不确定如何实现这一点(可以使用哪些线程同步对象/技术)我正在通过VS2010在Windows上开发此应用程序

这里有什么建议或替代方法吗?

更新:感谢大家的投入。遗憾的是,VS2010中没有可用的原子int。因此,以下是我根据您的意见计划做的事情。我要三种锁:在地图上:map_read_lock、map_write_lock关于元素:element_write_lock(对于每个元素)

现在,

在地图中查找元素时:获取map_read_lock(这将允许我同时查找)

当向映射添加/删除元素时:Get-map_write_lock(这将阻止容器的并发更新,我认为这是不推荐的)

更改值时:Get(map_read_lock&element_write_lock)(这将允许对不同值进行并行更改,但将防止对同一值进行并发更改。此外,在更新容器时将防止对值进行更改,反之亦然)

当一个线程增加学生A,另一个线程删除学生A?你需要即使只是在修改标记时也能锁定地图。或者你需要更复杂的事务管理(可能不是对于这样一个简单的案例来说是合理的)。

或者,您可以在地图上使用rwlock锁定地图中的每个元素。要修改标记,您需要映射上的读取锁定和元素上的排他锁定;到添加或删除一个学生,你就可以在地图上进行写锁定。但是这需要大量的额外资源。

  • 第一个问题:给学生打分的效率应该有多高
  • 第二个问题:真的需要多线程吗?如果这是为了效率,也许甚至于事无补

之后,您可以考虑Reader/Writer锁,其中Reader锁不是独占的。但要注意,因为实际的评分是书面的,所以你可能需要为每个学生另加一个锁来避免争论。

  1. 我只想将最后两个(添加/删除StudentName)活动排除在外(在映射中添加/删除元素时,不应并行执行其他活动)

为此,您需要在地图上锁定读写器。

  • 添加/删除需要独占访问(写入程序)
  • 访问学生需要(仅)共享访问(阅读器)
  1. 不允许多个线程访问映射的同一元素(这样多个线程就不能同时尝试增加/减少同一学生的分数)

这也相对简单:映射的每个元素都应该有自己的锁。这样,当访问元素而不修改映射结构时,您只需:

  • 在共享访问中锁定地图
  • 锁定单个元素(即每个元素都需要自己的锁)

从而并行地访问多个单独的元件。

Mark的特殊情况下,您也可以查看std::atomic。当一个简单的atomic可以做你想做的事情时,不需要使用锁:)