C++ 线程安全 vector .erase

标签 c++ multithreading vector thread-safety

我为 SFML 编写了一个线程渲染器,它采用指向可绘制对象的指针并将它们存储在要绘制每帧的 vector 中。开始向 vector 添加对象以及向 vector 删除对象经常会导致段错误 (SIGSEGV)。为了尝试解决这个问题,我会将需要删除/添加的对象添加到稍后删除的队列中(在绘制框架之前)。这似乎解决了这个问题,但最近我注意到,如果我一次添加许多对象(或足够快地添加/删除它们),我将得到相同的 SIGSEGV。

在 vector 中添加/删除时是否应该使用锁?

最佳答案

您需要了解 C++ 标准(以及可能并发系统的 C++2003 的实现)提供的线程安全保证。标准容器在以下意义上是线程安全的:

  1. 可以有多个并发线程读取同一个容器。
  2. 如果有一个线程修改容器,则不得有并发线程读取或写入同一容器。
  3. 不同的容器是相互独立的。

许多人误解容器的线程安全性,认为这些规则是由容器实现强加的:事实并非如此!您有责任遵守这些规则。

这些不是、实际上也不能由容器强加的原因是它们没有适合于此的接口(interface)。例如,考虑以下这段简单的代码:

if (!c.empty() {
    auto value = c.back();
    // do something with the read value
}

容器可以控制对empty()back()调用的访问。然而,在这些调用之间,它必然需要释放任何类型的同步设施,即,当线程尝试读取 c.back() 时,容器可能再次为空!基本上有两种方法可以解决这个问题:

  1. 如果并发线程可能会更改容器以跨越以某种形式相互依赖的整个访问范围,则需要使用外部锁定。
  2. 您将容器的接口(interface)更改为 monitors 。然而,容器接口(interface)根本不适合朝这个方向改变,因为监视器本质上只支持“即发即忘”风格的接口(interface)。

两种策略都有其优点,并且标准库容器显然支持第一种样式,即,在并发使用时需要外部锁定,并且可能有至少一个线程修改容器。如果一开始只有一个线程使用它们,则它们不需要任何类型的锁定(无论是内部锁定还是外部锁定)。这实际上就是他们设计的场景。为它们提供的线程安全保证是为了保证不使用非线程安全的内部设施,例如一个每个对象的迭代器对象或由多个线程共享的非线程安全的内存分配设施等.

回答原来的问题:是的,您需要使用外部同步,例如以互斥锁的形式,如果您在一个线程中修改容器并在另一个线程中读取它。

关于C++ 线程安全 vector .erase,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8959504/

相关文章:

c++ - ostream::write 不写整个结构?

c# - 使用 FLAUI 等待应用程序启动而不使用 Thread.Sleep()

vector - glsl 成员明智? : operator

c++ - 在 vector::resize() 和 vector::reserve() 之间选择

c++ - 使用迭代器从 vector 中查找子字符串

c++ - bsub 启动的作业的退出代码

c++ - 压缩嵌套循环

c++ - 如何在删除元素时防止重新散列 std::unordered_map?

c++ - 互斥体的存在是否有助于摆脱 volatile 关键字?

c++ - Windows C++中线程的退出代码