我对 std::set 的线程安全有疑问。
据我所知,我可以迭代一个集合并添加/删除成员,这不会使迭代器无效。
但考虑以下情况:
- 线程“A”迭代一组 shared_ptr<Type>
- 线程“B”偶尔会向这个集合添加项目。
我在程序运行时遇到了段错误,我不确定为什么会发生这种情况。缺乏线程安全是原因吗?
STL 没有内置线程支持,因此您必须使用自己的同步机制扩展 STL 代码才能在多线程环境中使用 STL。
例如看这里:链接文本
由于 set 是一个容器类,MSDN 有以下关于容器的线程安全性的说法。
单个对象是线程安全的,可以从多个线程中读取。例如,给定一个对象 A,从线程 1 和线程 2 同时读取 A 是安全的。
如果一个线程正在写入单个对象,则必须保护同一线程或其他线程上对该对象的所有读取和写入。例如,给定一个对象 A,如果线程 1 正在写入 A,那么必须阻止线程 2 读取或写入 A。
即使另一个线程正在读取或写入同一类型的不同实例,读取和写入一个类型的实例也是安全的。例如,给定相同类型的对象 A 和 B,如果在线程 1 中写入 A 而在线程 2 中读取 B 是安全的。
Dinkumware STL 文档包含有关该主题的以下段落。它可能(如文本中所示)对大多数实现都有效。
对于标准 C++ 库中定义的容器对象,例如 STL 容器和模板类 basic_string 的对象,此实现遵循为 SGI STL 阐明的广泛采用的做法:
多个线程可以安全地读取同一个容器对象。(容器对象中有不受保护的可变子对象。)
两个线程可以安全地操作相同类型的不同容器对象。(容器类型中没有不受保护的共享静态对象。)
如果至少有一个线程正在修改对象,则必须防止同时访问容器对象。(显而易见的同步原语,例如 Dinkum 线程库中的那些,不会被容器对象颠覆。)
因此,没有尝试确保容器对象上的原子操作是线程安全的;但是很容易在适当的粒度级别上创建线程安全的共享容器对象。
没有一个 STL 容器是线程安全的,std::set
尤其是不是。
在您的情况下,问题甚至不是真正的线程安全:您只需在多个线程之间共享一个对象(很好)并在一个线程中修改它(也很好)。但正如您已经说过的,修改容器会使其迭代器无效。这是否发生在同一个线程或不同线程中都无关紧要,因为它仍然是同一个容器。
哦!§23.1.2.8 声明插入不会使迭代器无效。
执行插入可能会导致向量重新分配其底层内存,而迭代器仍可能指向先前(但无效)的内存地址,从而导致段错误。
简单解释:如果线程 A 在容器中移动迭代器,它正在查看容器内部。如果线程 B 修改容器(即使是不会使 A 拥有的迭代器无效的操作),线程 A 可能会遇到麻烦,因为 B 正在欺骗容器内部,可能使它们处于(暂时)无效状态。这会导致线程 A 崩溃。
问题不在于迭代器本身。它当他们需要容器的数据结构以便找到你遇到麻烦的位置时。
就那么简单。
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.