【发布时间】:2010-11-24 15:06:09
【问题描述】:
我有一个关于 std::set 线程安全的问题。
据我所知,我可以迭代一个集合并添加/删除成员,这不会使迭代器无效。
但请考虑以下情况:
- 线程“A”迭代一组 shared_ptr
- 线程“B”偶尔会将项目添加到此集合中。
我在程序运行时遇到了段错误,我不确定为什么会发生这种情况。是缺乏线程安全的原因吗?
【问题讨论】:
我有一个关于 std::set 线程安全的问题。
据我所知,我可以迭代一个集合并添加/删除成员,这不会使迭代器无效。
但请考虑以下情况:
我在程序运行时遇到了段错误,我不确定为什么会发生这种情况。是缺乏线程安全的原因吗?
【问题讨论】:
没有一个 STL 容器是线程安全的,所以 std::set 尤其不是。
在您的情况下,问题甚至不是真正的线程安全:您只需在多个线程之间共享一个对象(很好)并在一个线程中修改它(也很好)。但正如你已经说过的,修改容器会使它的迭代器失效。这是否发生在同一个线程或不同线程中都无关紧要,因为它仍然是同一个容器。
天啊! §23.1.2.8 声明插入不会使迭代器无效。
【讨论】:
set、map、multiset 和multimap。 TR1 针对unordered_*(我正在查看的版本中的第 6.3.1.12-13 节)在插入/删除和迭代器有效性方面指定了自己的规则。这些要求确实比订购的同行要弱。
STL 没有内置线程支持,因此您必须扩展 STL 使用您自己的同步机制的代码来使用 STL 多线程环境。
例如看这里:link text
由于 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:
多个线程可以安全地读取同一个容器对象。 (有 nunprotected 内的可变子对象 一个容器对象。)
两个线程可以安全地操作不同的容器对象 同类型。 (没有 未受保护的共享静态对象 在容器类型中。)
您必须防止同时访问容器 如果至少有一个线程是对象 修改对象。 (明显的 同步原语,例如 Dinkum 线程库中的那些, 不会被容器颠覆 对象。)
因此,没有尝试确保 容器上的原子操作 对象是线程安全的;但它是 很容易制作共享容器 线程安全的对象 适当的粒度级别。
【讨论】:
简单解释:如果线程 A 在容器中移动迭代器,它正在查看容器内部。如果线程 B 修改容器(即使是不会使 A 拥有的迭代器无效的操作),线程 A 可能会遇到麻烦,因为 B 正在欺骗容器内部,可能使它们处于(暂时)无效状态。这会导致线程 A 崩溃。
问题不在于迭代器本身。当他们需要容器的数据结构以便找到您遇到麻烦的位置时。
就这么简单。
【讨论】:
是的。处理这种情况的一种方法是让每个线程在访问同一个集合对象之前锁定一个共享互斥体。确保使用 RAII 技术来锁定和解锁互斥体。
【讨论】:
执行插入会导致向量重新分配其底层内存,而迭代器仍可能指向先前(但无效)的内存地址,从而导致段错误。
【讨论】: