41

我对 std::set 的线程安全有疑问。

据我所知,我可以迭代一个集合并添加/删除成员,这不会使迭代器无效。

但考虑以下情况:

  • 线程“A”迭代一组 shared_ptr<Type>
  • 线程“B”偶尔会向这个集合添加项目。

我在程序运行时遇到了段错误,我不确定为什么会发生这种情况。缺乏线程安全是原因吗?

4

6 回答 6

32

STL 没有内置线程支持,因此您必须使用自己的同步机制扩展 STL 代码才能在多线程环境中使用 STL。

例如看这里:链接文本

由于 set 是一个容器类,MSDN 有以下关于容器的线程安全性的说法。

单个对象是线程安全的,可以从多个线程中读取。例如,给定一个对象 A,从线程 1 和线程 2 同时读取 A 是安全的。

如果一个线程正在写入单个对象,则必须保护同一线程或其他线程上对该对象的所有读取和写入。例如,给定一个对象 A,如果线程 1 正在写入 A,那么必须阻止线程 2 读取或写入 A。

即使另一个线程正在读取或写入同一类型的不同实例,读取和写入一个类型的实例也是安全的。例如,给定相同类型的对象 A 和 B,如果在线程 1 中写入 A 而在线程 2 中读取 B 是安全的。

于 2009-09-01T12:13:56.433 回答
24

Dinkumware STL 文档包含有关该主题的以下段落。它可能(如文本中所示)对大多数实现都有效。

对于标准 C++ 库中定义的容器对象,例如 STL 容器和模板类 basic_string 的对象,此实现遵循为 SGI STL 阐明的广泛采用的做法:

多个线程可以安全地读取同一个容器对象。(容器对象中有不受保护的可变子对象。)

两个线程可以安全地操作相同类型的不同容器对象。(容器类型中没有不受保护的共享静态对象。)

如果至少有一个线程正在修改对象,则必须防止同时访问容器对象。(显而易见的同步原语,例如 Dinkum 线程库中的那些,不会被容器对象颠覆。)

因此,没有尝试确保容器对象上的原子操作是线程安全的;但是很容易在适当的粒度级别上创建线程安全的共享容器对象。

于 2009-09-01T13:10:26.393 回答
12

没有一个 STL 容器是线程安全的,std::set尤其是不是。

在您的情况下,问题甚至不是真正的线程安全:您只需在多个线程之间共享一个对象(很好)并在一个线程中修改它(也很好)。但正如您已经说过的,修改容器会使其迭代器无效。这是否发生在同一个线程或不同线程中都无关紧要,因为它仍然是同一个容器

哦!§23.1.2.8 声明插入不会使迭代器无效。

于 2009-09-01T12:03:41.847 回答
2

执行插入可能会导致向量重新分配其底层内存,而迭代器仍可能指向先前(但无效)的内存地址,从而导致段错误。

于 2013-01-17T22:14:52.247 回答
2

简单解释:如果线程 A 在容器中移动迭代器,它正在查看容器内部。如果线程 B 修改容器(即使是不会使 A 拥有的迭代器无效的操作),线程 A 可能会遇到麻烦,因为 B 正在欺骗容器内部,可能使它们处于(暂时)无效状态。这会导致线程 A 崩溃。

问题不在于迭代器本身。它当他们需要容器的数据结构以便找到你遇到麻烦的位置时。

就那么简单。

于 2009-09-01T13:57:45.713 回答
1

Yes. One way to handle this situation is to have each thread lock a shared mutex before accessing the same set object. Make sure you use RAII techniques to lock and unlock the mutex.

于 2009-09-01T18:11:57.760 回答