【问题标题】:C++ Thread safe vector.eraseC++ 线程安全向量.erase
【发布时间】:2012-01-22 06:38:32
【问题描述】:
我为 SFML 编写了一个线程渲染器,它接受指向可绘制对象的指针并将它们存储在一个向量中以绘制每一帧。开始向向量中添加对象和向向量中删除对象会经常导致分段错误 (SIGSEGV)。为了尝试解决这个问题,我会将需要删除/添加到队列中的对象添加到稍后要删除的队列中(在绘制框架之前)。这似乎解决了它,但最近我注意到如果我一次添加许多对象(或添加/删除它们足够快),我将获得相同的 SIGSEGV。
当我从向量中添加/删除时我应该使用锁吗?
【问题讨论】:
标签:
c++
multithreading
vector
thread-safety
【解决方案1】:
您需要了解 C++ 标准(以及可能的并发系统的 C++2003 实现)提供的线程安全保证。标准容器在以下意义上是线程安全的:
- 允许多个并发线程读取同一个容器。
- 如果有一个线程修改容器,则不应有并发线程读取或写入同一容器。
- 不同的容器相互独立。
很多人误解容器的线程安全意味着这些规则是由容器实现强加的:它们不是!您有责任遵守这些规则。
这些不是,实际上不能,由容器强加的原因是它们没有适合这个的接口。例如,考虑以下简单的代码:
if (!c.empty() {
auto value = c.back();
// do something with the read value
}
容器可以控制对empty()和back()的调用的访问。但是,在这些调用之间,它必然需要释放任何类型的同步工具,即当线程尝试读取c.back() 时,容器可能再次为空!处理这个问题基本上有两种方法:
- 如果并发线程可能会更改容器以跨越以某种形式相互依赖的整个访问范围,则需要使用外部锁定。
- 您将容器的接口更改为monitors。然而,容器接口根本不适合朝这个方向改变,因为监视器本质上只支持“即发即弃”风格的接口。
这两种策略都有其优点,标准库容器显然支持第一种样式,即它们在并发使用时需要外部锁定,并且可能至少有一个线程修改容器。如果一开始只有一个线程使用它们,它们不需要任何类型的锁定(内部或外部)。这实际上是他们设计的场景。为它们提供的线程安全保证是为了保证没有使用不是线程安全的内部设施,例如一个每个对象的迭代器对象或由多个线程共享的内存分配设施,而不是线程安全的,等等.
回答原始问题:是的,您需要使用外部同步,例如以互斥锁的形式,如果您在一个线程中修改容器并在另一个线程中读取它。
【解决方案2】:
当我从向量中添加/删除时我应该使用锁吗?
是的。如果您同时使用来自两个线程的向量并重新分配,则后备分配可能会被换出并在另一个线程的脚后释放。另一个线程将读取/写入已释放的内存,或者正在使用的内存用于另一个不相关的分配。