std::map 和线程安全的奇怪问题

Posted

技术标签:

【中文标题】std::map 和线程安全的奇怪问题【英文标题】:Odd issue with std::map and thread safety 【发布时间】:2011-03-07 10:54:41 【问题描述】:

现在这不是什么大问题,因为我已经实现了自己的集合,但对这个仍然有点好奇。

我有一个单例,它提供对各种公共组件的访问,它保存这些组件的实例和线程 ID,因此每个线程应该(并且确实,我检查过)拥有它自己的组件实例,例如 Oracle 数据库访问图书馆。

当运行系统(这是一个由 C# 应用程序调用的 C++ 库)时,有多个传入请求似乎一切正常运行了一段时间,但随后它因 AccessViolation 异常而崩溃。单步执行调试器时,问题似乎是当一个线程完成并清除它的会话信息(保存在 std::map 对象中)时,另一个线程的单独集合实例中保存的会话信息似乎也被清除了。

这是其他人遇到过或知道的吗?我试过环顾四周,但找不到任何关于此类问题的信息。

干杯

【问题讨论】:

“我有一个单身人士......”这就是问题所在。 :) 老实说,我很惊讶没有人早点这么说:) 【参考方案1】:

标准 C++ 容器不太关心线程安全。您的代码听起来像是从两个不同的线程修改地图实例,或者在一个线程中修改地图并在另一个线程中读取它。这显然是错误的。使用一些锁定原语来同步线程之间的访问。

【讨论】:

这就是我最初的想法,但检查调试器中的信息我可以看到两个映射都位于不同的内存位置。那么我在哪里:(线程 1 -> 映射 a)和(线程 2 -> 映射 b)当线程 1 清除“映射 a”时,我最终也会清除“映射 b”? @daz-fuller:如果它们实际上是独立的maps,那么您可能有更深层次的问题。回到调试器,直到您了解问题所在,或者您可能只是隐藏错误而不是修复它。 @daz-fuller: 你用什么编译器来编译 C++ 代码?什么版本? @daz-fuller:另外,您是否将 C++ 代码与正确的运行时库链接?支持版本的线程? 它是使用 MSVC(版本 8,工作室 2005)和 GCC (3.4.5) 构建的。它是一个静态链接库,并且正在链接到多线程版本。【参考方案2】:

如果您只想为每个线程创建一个单独的对象,您可能需要查看boost::thread_specific_ptr

【讨论】:

我经常这样做,不幸的是,由于项目的性质,我们仅限于 STL 和我们自己编写的任何东西:( 我建议你看一下thread_specific_ptr的代码然后自己写。【参考方案3】:

您如何管理为每个线程提供自己的会话信息?在下面的某个地方,您有管理这些对象生命周期的类,而这似乎是出错的地方。

【讨论】:

有一个处理这些项目的单例,当它被要求提供一个调试记录器时,它会检查该线程是否存在一个,如果不存在则创建一个。那位工作正常,问题在于创建的线程特定实例。一种特定类型具有清除方法。所以“线程A”有“对象A”和“映射A”,“线程B”有“对象B”和“映射B”,出于某种原因,每隔一段时间调用“对象A”上的清除方法会影响“地图 A”和“地图 B” 所以它检查了某种表格。然后在线程完成后将其从某种表中删除。因此,该表被多个线程使用。还要注意迭代器可能会失效。 如果静态查找表有问题,那么我希望清除错误的地图,但不会同时清除两者。另外我希望修改一些错误的 Objects 属性,但事实并非如此,正确的对象是否已修改?

以上是关于std::map 和线程安全的奇怪问题的主要内容,如果未能解决你的问题,请参考以下文章

线程安全std :: map:锁定整个地图和各个值[重复]

std::map 上可能的线程不安全操作

多线程访问一个 std::map 会导致不安全的行为吗?

std::map 访问线程是不是安全,如果它的迭代器永远不会失效

如何以线程安全的方式使用`std::unordered_map`?

不断迭代的线程安全的 Unordered_map