是 C++ std::set 线程安全?
我对 std::set 的线程安全有疑问。
据我所知,我可以迭代一个集合并添加/删除成员,这不会使迭代器无效。
但请考虑以下场景:
- 线程“A”迭代一组shared_ptr
。 - 线程“B”偶尔会向该集合添加项目。
我在程序运行时遇到了段错误,但我不确定为什么会发生这种情况。缺乏线程安全是原因吗?
I've a question about the thread safety of std::set.
As far as I know I can iterate over a set and add/erase members and that doesn't invalidate the iterators.
But consider following scenario:
- thread 'A' iterates over a set of shared_ptr<Type>
- thread 'B' occasionally adds items to this set.
I've experienced segfaults as the program runs and I'm not sure why this happens. Is lack of thread safety the cause?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
STL 没有内置线程支持,因此您必须扩展 STL
使用您自己的同步机制编写代码以在中使用STL
多线程环境。
例如,请查看此处:链接文本
由于set是一个容器类,MSDN对容器的线程安全有如下说法。
单个对象对于从多个线程读取是线程安全的。例如,给定一个对象 A,同时从线程 1 和线程 2 读取 A 是安全的。
如果一个线程正在写入单个对象,则同一线程或其他线程上对该对象的所有读取和写入都必须受到保护。例如,给定一个对象 A,如果线程 1 正在写入 A,则必须阻止线程 2 读取或写入 A。
即使另一个线程正在读取或写入某一类型的一个实例,读取和写入也是安全的。写入同一类型的不同实例。例如,给定相同类型的对象 A 和 B,如果 A 在线程 1 中写入,B 在线程 2 中读取,则这是安全的。
STL has no built in thread support, so you'll have to extend the STL
code with your own synchronization mechanisms to use STL in
a multithreaded environment.
For example look here: link text
Since set is a container class MSDN has following to say about the thread safety of the containers.
A single object is thread safe for reading from multiple threads. For example, given an object A, it is safe to read A from thread 1 and from thread 2 simultaneously.
If a single object is being written to by one thread, then all reads and writes to that object on the same or other threads must be protected. For example, given an object A, if thread 1 is writing to A, then thread 2 must be prevented from reading from or writing to A.
It is safe to read and write to one instance of a type even if another thread is reading or writing to a different instance of the same type. For example, given objects A and B of the same type, it is safe if A is being written in thread 1 and B is being read in thread 2.
Dinkumware STL 文档包含有关该主题的以下段落。它可能(如文本中所示)对于大多数实现都有效。
The Dinkumware STL-Documentation contains the follwing paragraph about that topic. Its probably (as indicated in the text) valid for most implementations.
没有一个 STL 容器是线程安全的,所以
std::set
尤其不是。在您的情况下,问题甚至不是真正的线程安全:您只需在多个线程之间共享一个对象(很好)并在一个线程中修改它(也很好)。但正如您已经说过的,修改容器会使其迭代器失效。无论这种情况发生在同一个线程中还是不同的线程中,都没有什么影响,因为它仍然同一个容器。天啊! §23.1.2.8 规定插入不会使迭代器失效。
None of the STL containers is thread safe, so
std::set
in particular isn’t.In your case, the issue isn’t even really thread safety, though: You simply share an object across multiple threads (fine) and modify it in one thread (fine as well). But as you’ve already said, modifying the container invalidates its iterators. Whether this happens in the same thread or in a different thread is of no consequence since it’s still the same container.D'oh! §23.1.2.8 states that inserting doesn’t invalidate iterators.
简单解释:如果线程 A 在容器中移动迭代器,它就会查看容器内部。如果线程 B 修改容器(即使是一个不会使 A 所具有的迭代器无效的操作),线程 A 可能会遇到麻烦,因为 B 正在欺骗容器内部,可能使它们处于(暂时)无效状态。这会导致线程 A 崩溃。
问题不在于迭代器本身。当他们需要容器的数据结构来找到你遇到麻烦的位置时。
就这么简单。
Simple explanation: If thread A is moving iterators through the container, it's looking at container internals. If thread B modifies the container (even an operation that doesn't invalidate the iterator that A has), thread A can run into trouble because B is fooling with the container internals, possibly having them in a (temporarily) invalid state. This causes crashes in thread A.
The problem ISN'T the iterators themselves. It when they need the container's data structures in order to find the position that you get into trouble.
Simple as that.
执行插入可能会导致向量重新分配其底层内存,而迭代器可能仍指向先前(但无效)的内存地址,从而导致段错误。
Performing an insertion can cause the vector to reallocate its underlying memory while iterator may still point to the previous (but invalid) memory address, leading to segment fault.
是的。处理这种情况的一种方法是让每个线程在访问同一个集合对象之前锁定一个共享互斥锁。确保使用 RAII 技术来锁定和解锁互斥体。
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.