【问题标题】:Efficient thread synchronization technique for std::mapstd::map 的高效线程同步技术
【发布时间】:2013-08-30 10:30:33
【问题描述】:

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

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

  1. 找到StudentName。如果存在,请增加其Marks
  2. Marks 减少到StudentName
  3. StudentName 添加到地图中。
  4. 从地图中删除StudentName

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

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

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

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

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

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

更新: 感谢大家的意见。不幸的是,VS2010 中没有可用的原子整数。所以,这就是我计划根据你的意见做的事情。我将拥有三种锁: 在地图上:map_read_lock、map_write_lock 关于元素:element_write_lock (对于每个元素)

现在,

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

在map中添加/删除元素时:获取map_write_lock (这样会阻止容器的并发更新,我认为不推荐)

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

【问题讨论】:

  • 使用互斥锁,即 boost::mutex 和 scope_lock。而最重要的锁只在你必须的时候并且只用了一点时间。例如,当您将元素插入地图并获取值时。就这样。顺便说一句,考虑使用 hashmap,即 std::unordered_map - 在某些情况下更有效。
  • Provided STL map are not thread safe, (i.e. no other thread should access map when it is being updated) 不完全是,我已经在这里回答了类似的问题stackoverflow.com/questions/16130494/… 如果您不修改地图本身,则可以访问它的元素以进行读写,只要有给定元素没有竞争条件。
  • @LukeCodeBaker 他需要以某种方式同步他的所有操作。
  • 您是如何使用 C++ 实现 map_read_lock、map_write_lock.. 的?几个 std::mutex?
  • @Johy 我用过libuv's thread sync functions。对于读锁uv_rwlock_rdlock and for write uv_rwlock_wrlock,他们在内部使用了相关操作系统的锁定功能,而不是标准,但我不记得了。相当古老的回忆:) 但你可以参考他们的文档。感谢您的提问。

标签: c++ multithreading dictionary std stdmap


【解决方案1】:

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

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

【讨论】:

  • 如果学生被引用计数并且“删除”只是“从地图中删除”(取决于引用计数以实际释放数据),那么您在修改分数时还需要锁定地图吗?
  • @WanderingLogic 学生如何被引用计数?它们存在于地图中。
  • 我们即将为Marks 类型的每个对象添加一个互斥锁。所以我假设我们可以同时使Marks 成为结构的智能指针。 (或者我想,由于 Marks 目前只是一个整数,我们可以将其设为 std::atomic_int,然后在地图上使用 rwlock。)
  • 这里当然没有理由在任何地方使用动态分配,因此也没有智能指针。不过,使用std::atomic_int 的想法很好;无论开销如何,它都比每个条目使用互斥锁要少得多。
  • 那么,“保留 std::atomic_int 类型的标记并在地图上使用 rwlock”是最好的解决方案吗?另外,我们是否在 STL 中内置了 rwlock 函数,或者您指的是使用 boost 等?
【解决方案2】:
  • 第一个问题:给学生评分的效率应该如何?
  • 第二个问题:真的需要多线程吗?如果是为了效率,也许它甚至没有帮助。

之后,您可以考虑Reader/Writer lock,其中 Reader 锁不是独占的。但请注意,因为实际评分是写作,所以您可能需要为每个学生设置另一个锁以避免争用。

【讨论】:

  • 学生人数会非常高(20k 或更多),决定增加/减少分数的输入来自不同的来源。因此,多线程。
【解决方案3】:
  1. 我只想将最后两个(添加/删除 StudentName)活动排除在外(在向地图添加/删除元素时,不应并行执行其他活动)

为此,您需要在地图上设置读取器/写入器锁。

  • 添加/删除需要独占访问权限(作者)
  • 访问学生需要(仅)共享访问(阅读器)
  1. 不允许多个线程访问同一地图元素(以免多个线程同时尝试增加/减少同一学生的分数)

这也是相对简单的:地图的每个元素都应该有自己的锁。这样,在不修改地图结构的情况下访问元素时,您可以:

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

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

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

【讨论】:

  • std::atomic 将有助于标记的递增/递减。但是如果另一个线程删除了地图元素呢?猜猜我们还需要锁吗?如有错误请指正。
  • @Andrew:这就是地图本身的读/写锁的原因。要递增/递减,您需要锁定(读取模式),因此在此期间排除了对地图本身的任何添加/删除操作。
  • 现代 C++ 中“每个元素都需要自己的锁”的最佳实现是什么?几个 std::mutex 或 shared_mutex?
  • @Johy:鉴于应用程序的基本功能,每个元素一个 std::mutex 就足够了......尽管考虑到 C++11 的历史,我真的鼓励人们使用 @ 987654325@ 标记它会更轻量级。
  • @MatthieuM。但是如果我事先不知道容器中有多少元素,我该如何使用原子,因为我无法制作它们的容器
猜你喜欢
  • 1970-01-01
  • 2011-06-12
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
  • 2014-09-06
  • 1970-01-01
  • 2014-05-26
  • 1970-01-01
相关资源
最近更新 更多